From 81dbe2875b649f782ae2244a466eb824b08b6e56 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 00:11:38 +0300 Subject: [PATCH 1/6] Huge refactoring * Remove `libtest` module but keep some of the tests from it * Make the project a single-module one * Add `AbstractLincheckTest` abstraction to manage most of the tests in a single place and write them easier * Rewrite some of the code into Kotlin * Reuse verifiers properly * Handle deadlocks/livelocks and unexpected exceptions properly * etc --- README.md | 14 +- libtest/pom.xml | 59 - .../lock/free/queue/BlockingQueueAdapter.java | 51 - .../github/lock/free/queue/LockFreeQueue.java | 90 - .../java/com/github/lock/free/queue/Node.java | 35 - .../github/lock/free/queue/SimpleQueue.java | 31 - .../high_scale_lib/old/AbstractEntry.java | 74 - .../old/ConcurrentAutoTable.java | 298 --- .../cliffc/high_scale_lib/old/Counter.java | 61 - .../high_scale_lib/old/IntIterator.java | 28 - .../old/NonBlockingHashMap.java | 1318 ------------ .../old/NonBlockingHashMapLong.java | 1220 ----------- .../old/NonBlockingHashSet.java | 108 - .../old/NonBlockingHashtable.java | 1308 ------------ .../old/NonBlockingIdentityHashMap.java | 1285 ------------ .../high_scale_lib/old/NonBlockingSetInt.java | 637 ------ .../cliffc/high_scale_lib/old/UtilUnsafe.java | 50 - .../handrolled/QueueThroughputBusy.java | 115 - .../handrolled/QueueThroughputYield.java | 117 -- .../queues/benchmarks/jmh/QueueBenchmark.java | 47 - .../benchmarks/jmh/QueueRoundTripLatency.java | 160 -- .../benchmarks/jmh/QueueThroughputBusy.java | 116 - .../benchmarks/jmh/QueueThroughputYield.java | 118 -- .../benchmarks/jmh/SingleThreadedOffer.java | 68 - .../benchmarks/jmh/SingleThreadedPoll.java | 69 - .../common/CircularArray1ReadWrite.java | 88 - .../common/CircularArray2ReadWrite.java | 88 - .../common/CircularArray3ReadWrite.java | 88 - .../common/CircularArray4ReadWrite.java | 89 - .../queues/common/CircularArrayQueue1.java | 50 - .../queues/common/CircularArrayQueue2.java | 56 - .../queues/common/CircularArrayQueue3.java | 59 - .../queues/common/CircularArrayQueue4.java | 87 - .../java/psy/lob/saw/queues/common/Pow2.java | 32 - .../saw/queues/common/SPSCQueueFactory.java | 74 - .../psy/lob/saw/queues/ff/FastFlowQueue1.java | 134 -- .../psy/lob/saw/queues/ff/FastFlowQueue2.java | 148 -- .../lob/saw/queues/lamport/LamportQueue1.java | 118 -- .../lob/saw/queues/lamport/LamportQueue2.java | 123 -- .../lob/saw/queues/lamport/LamportQueue3.java | 123 -- .../lob/saw/queues/lamport/LamportQueue4.java | 133 -- .../lob/saw/queues/lamport/LamportQueue5.java | 166 -- .../saw/queues/lamport/VolatileLongCell.java | 57 - .../psy/lob/saw/queues/thompson/LongCell.java | 34 - .../saw/queues/thompson/ThompsonQueue1.java | 158 -- .../saw/queues/thompson/ThompsonQueue2.java | 159 -- .../saw/queues/thompson/ThompsonQueue3.java | 223 -- libtest/src/main/java/romix/scala/None.java | 34 - libtest/src/main/java/romix/scala/Option.java | 48 - libtest/src/main/java/romix/scala/Some.java | 45 - .../collection/concurrent/BasicNode.java | 42 - .../collection/concurrent/CNodeBase.java | 57 - .../scala/collection/concurrent/Gen.java | 39 - .../collection/concurrent/INodeBase.java | 57 - .../scala/collection/concurrent/ListMap.java | 251 --- .../scala/collection/concurrent/MainNode.java | 58 - .../scala/collection/concurrent/Pair.java | 62 - .../scala/collection/concurrent/TrieMap.java | 1863 ----------------- .../java/tests/custom/counter/Counter.java | 27 - .../tests/custom/counter/CounterCorrect2.java | 38 - .../java/tests/custom/counter/CounterGet.java | 39 - .../tests/custom/counter/CounterWrong0.java | 38 - .../tests/custom/counter/CounterWrong1.java | 37 - .../tests/custom/counter/CounterWrong2.java | 36 - .../main/java/tests/custom/queue/Queue.java | 28 - .../custom/queue/QueueEmptyException.java | 33 - .../custom/queue/QueueFullException.java | 33 - .../tests/custom/queue/QueueSynchronized.java | 77 - .../java/tests/custom/queue/QueueWrong1.java | 77 - .../java/tests/custom/queue/QueueWrong2.java | 77 - .../java/tests/custom/queue/QueueWrong3.java | 77 - .../java/tests/custom/transfer/Accounts.java | 29 - .../tests/custom/transfer/AccountsWrong1.java | 74 - .../tests/custom/transfer/AccountsWrong2.java | 74 - .../tests/custom/transfer/AccountsWrong3.java | 74 - .../tests/custom/transfer/AccountsWrong4.java | 66 - .../src/main/java/thesis_example/Counter.java | 27 - .../java/thesis_example/CounterCorrect1.java | 36 - .../java/thesis_example/CounterCorrect2.java | 39 - .../java/thesis_example/CounterWrong1.java | 37 - .../java/thesis_example/CounterWrong2.java | 36 - .../java/thesis_example/QueueCorrect.java | 66 - .../main/java/z/annotation/InternalUse.java | 49 - .../main/java/z/annotation/NotThreadSafe.java | 49 - .../z/annotation/PerformanceDegraded.java | 49 - .../main/java/z/annotation/ThreadSafe.java | 49 - .../main/java/z/channel/BroadcastChannel.java | 47 - libtest/src/main/java/z/channel/Channel.java | 50 - libtest/src/main/java/z/channel/Channels.java | 73 - .../main/java/z/channel/GenericHyperLoop.java | 272 --- .../main/java/z/channel/GenericMPMCQueue.java | 212 -- .../src/main/java/z/channel/IntHyperLoop.java | 281 --- .../main/java/z/channel/LongHyperLoop.java | 296 --- .../src/main/java/z/channel/MPMCQueue.java | 199 -- .../src/main/java/z/channel/ReceivePort.java | 51 - libtest/src/main/java/z/evil/Intrinsics.java | 60 - .../exception/ContractViolatedException.java | 59 - .../java/z/function/ExceptionalFunction0.java | 58 - .../java/z/function/ExceptionalRunnable.java | 55 - .../src/main/java/z/function/Function0.java | 56 - .../src/main/java/z/function/Function1.java | 56 - .../src/main/java/z/function/Function10.java | 56 - .../src/main/java/z/function/Function11.java | 56 - .../src/main/java/z/function/Function2.java | 56 - .../src/main/java/z/function/Function3.java | 56 - .../src/main/java/z/function/Function4.java | 56 - .../src/main/java/z/function/Function5.java | 56 - .../src/main/java/z/function/Function6.java | 56 - .../src/main/java/z/function/Function7.java | 56 - .../src/main/java/z/function/Function8.java | 56 - .../src/main/java/z/function/Function9.java | 56 - .../src/main/java/z/function/Functions.java | 61 - libtest/src/main/java/z/function/Pipe.java | 56 - .../src/main/java/z/function/Pipeline.java | 84 - .../java/z/function/ThrowableFunction0.java | 58 - .../java/z/function/ThrowableRunnable.java | 55 - .../java/z/function/ToBooleanFunction0.java | 55 - .../main/java/z/function/ToByteFunction0.java | 54 - .../main/java/z/function/ToCharFunction0.java | 54 - .../java/z/function/ToDoubleFunction0.java | 54 - .../java/z/function/ToFloatFunction0.java | 54 - .../main/java/z/function/ToIntFunction0.java | 55 - .../main/java/z/function/ToLongFunction0.java | 55 - .../java/z/function/ToShortFunction0.java | 54 - libtest/src/main/java/z/module/License.java | 89 - libtest/src/main/java/z/module/Module.java | 66 - .../main/java/z/offheap/buffer/Buffer.java | 658 ------ .../offheap/buffer/BufferStateException.java | 58 - .../main/java/z/offheap/buffer/Buffers.java | 335 --- .../java/z/offheap/buffer/ByteBuffer.java | 103 - .../buffer/LittleEndianOrderBuffer.java | 69 - .../z/offheap/buffer/NativeOrderBuffer.java | 69 - .../z/offheap/buffer/NetworkOrderBuffer.java | 69 - .../java/z/offheap/zmalloc/Allocator.java | 1176 ----------- libtest/src/main/java/z/util/Contracts.java | 107 - .../src/main/java/z/util/MethodHandles.java | 63 - .../src/main/java/z/util/SystemProperty.java | 211 -- libtest/src/main/java/z/util/Throwables.java | 166 -- libtest/src/main/java/z/util/Unsafes.java | 633 ------ .../z/util/concurrent/ThreadLocalPool.java | 214 -- .../concurrent/ThreadLocalValueHolder.java | 84 - .../main/java/z/util/primitives/Bytes.java | 384 ---- .../main/java/z/util/primitives/Chars.java | 586 ------ .../src/main/java/z/util/primitives/Ints.java | 562 ----- .../main/java/z/util/primitives/Longs.java | 630 ------ .../java/z/util/primitives/Primitives.java | 154 -- .../main/java/z/util/primitives/Shorts.java | 594 ------ .../java/z/util/primitives/UnsignedBytes.java | 450 ---- .../java/z/util/primitives/UnsignedInts.java | 263 --- .../java/z/util/primitives/UnsignedLongs.java | 379 ---- .../kotlinx/lincheck/tests/LockFreeDeque.java | 257 --- .../lincheck/tests/LockFreeDequeTest.kt | 49 - .../tests/boundary/BitVectorCorrect1.java | 54 - .../lincheck/tests/boundary/MapCorrect1.java | 60 - .../lincheck/tests/boundary/SetCorrect1.java | 52 - .../tests/custom/counter/CounterGetTest.java | 55 - .../tests/custom/counter/CounterTest1.java | 45 - .../tests/custom/counter/CounterTest2.java | 45 - .../tests/custom/counter/CounterTest3.java | 45 - .../tests/custom/counter/CounterTest4.java | 45 - .../custom/queue/WrapperQueueCorrect1.java | 53 - .../custom/queue/WrapperQueueWrong1.java | 52 - .../custom/queue/WrapperQueueWrong2.java | 53 - .../custom/queue/WrapperQueueWrong3.java | 53 - .../tests/custom/transfer/AccountsTest.java | 95 - .../tests/guava/MultisetCorrect1.java | 53 - .../lincheck/tests/guava/SetCorrect1.java | 55 - .../blocking_queue/BlockingQueueTest1.java | 65 - .../tests/juc/hash_map/HashMapTest.java | 63 - .../tests/linearizability/MutexStressTest.kt | 57 - .../tests/lockfreequeue/QueueCorrect1.java | 53 - .../lincheck/tests/romix/TrieCorrect1.java | 56 - .../tests/zchannel/QueueCorrect1.java | 53 - .../tests/zchannel/QueueCorrect2.java | 53 - lincheck/pom.xml | 44 - .../kotlinx/lincheck/LinChecker.java | 195 -- .../kotlinx/lincheck/strategy/Strategy.java | 89 - .../strategy/stress/StressStrategy.java | 81 - .../linearizability/SequentialIntChannel.kt | 144 -- .../LockFreeMPSCQuiescentConsistentQueue.kt | 253 --- ...kFreeMPSCQuiescentConsistentQueueTest.java | 61 - .../SerializableQueueSimulation.kt | 42 - .../SerializableQueueSimulationTest.kt | 63 - pom.xml | 49 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 2 +- .../kotlinx/lincheck/CTestConfiguration.java | 8 +- .../kotlinx/lincheck/CTestStructure.java | 0 .../CancellabilitySupportTransformer.kt | 0 .../lincheck/ExecutionClassLoader.java | 0 .../jetbrains/kotlinx/lincheck/LinChecker.kt | 192 ++ .../lincheck/LincheckAssertionError.kt | 31 +- .../jetbrains/kotlinx/lincheck/Options.java | 1 - .../jetbrains/kotlinx/lincheck/Reporter.kt | 66 +- .../org/jetbrains/kotlinx/lincheck/Result.kt | 0 .../lincheck/TransformationClassLoader.java | 0 .../org/jetbrains/kotlinx/lincheck/Utils.kt | 4 +- .../lincheck/annotations/LogLevel.java | 4 +- .../lincheck/annotations/OpGroupConfig.java | 0 .../lincheck/annotations/Operation.java | 2 +- .../kotlinx/lincheck/annotations/Param.java | 0 .../lincheck/execution/ActorGenerator.java | 0 .../execution/ExecutionGenerator.java | 0 .../lincheck/execution/ExecutionResult.kt | 0 .../lincheck/execution/ExecutionScenario.java | 7 +- .../kotlinx/lincheck/execution/HBClock.kt | 4 +- .../execution/RandomExecutionGenerator.java | 0 .../kotlinx/lincheck/paramgen/ByteGen.java | 0 .../kotlinx/lincheck/paramgen/DoubleGen.java | 0 .../kotlinx/lincheck/paramgen/FloatGen.java | 0 .../kotlinx/lincheck/paramgen/IntGen.java | 0 .../kotlinx/lincheck/paramgen/LongGen.java | 0 .../lincheck/paramgen/ParameterGenerator.java | 0 .../kotlinx/lincheck/paramgen/ShortGen.java | 0 .../kotlinx/lincheck/paramgen/StringGen.java | 0 .../lincheck/runner/InvocationResult.kt | 50 + .../lincheck/runner/ParallelThreadsRunner.kt | 34 +- .../kotlinx/lincheck/runner/Runner.java | 34 +- .../lincheck/runner/TestThreadExecution.java | 2 - .../runner/TestThreadExecutionGenerator.java | 0 .../lincheck/strategy/FailedIteration.kt | 53 + .../lincheck/strategy/ManagedStrategy.java | 30 +- .../strategy/ManagedStrategyHolder.java | 0 .../strategy/ManagedStrategyTransformer.java | 0 .../kotlinx/lincheck/strategy/Strategy.kt | 43 + .../randomswitch/RandomSwitchCTest.java | 0 .../RandomSwitchCTestConfiguration.java | 8 +- .../randomswitch/RandomSwitchOptions.java | 0 .../randomswitch/RandomSwitchStrategy.java | 35 +- .../lincheck/strategy/stress/StressCTest.java | 0 .../stress/StressCTestConfiguration.java | 8 +- .../strategy/stress/StressOptions.java | 0 .../strategy/stress/StressStrategy.kt | 81 + .../lincheck/verifier/AbstractLTSVerifier.kt | 0 .../lincheck/verifier/CachedVerifier.java | 0 .../DummySequentialSpecification.java | 0 .../lincheck/verifier/EpsilonVerifier.java | 10 +- .../kotlinx/lincheck/verifier/LTS.kt | 2 +- .../verifier/SerializabilityVerifier.kt | 0 .../kotlinx/lincheck/verifier/Verifier.java | 0 .../lincheck/verifier/VerifierState.kt | 0 .../LinearizabilityVerifier.kt | 0 .../quiescent/QuiescentConsistencyVerifier.kt | 0 .../lincheck/test/AbstractLincheckTest.kt | 58 + .../lincheck/test/AlmostEmptyScenarioTest.kt | 0 .../lincheck/test/ExceptionAsResultTest.java | 0 .../test/FailedScenarioMinimizationTest.kt | 13 +- .../kotlinx/lincheck/test/HangingTest.kt | 17 +- .../lincheck/test/NonParallelOpGroupTest.java | 41 +- .../test/OperationsInAbstractClassTest.kt | 0 .../lincheck/test/ResultAsParameterTest.java | 72 +- .../kotlinx/lincheck/test/RunOnceTest.java | 6 +- .../test/StateEquivalenceImplCheckTest.kt | 2 +- .../test/runner/CancellationHandlingTest.kt | 0 .../test/runner/DeadlockOnSynchronizedTest.kt | 57 + .../ParallelThreadsRunnerEasyExceptionTest.kt | 29 +- .../test/runner}/ResumingFollowUpTest.kt | 2 +- .../runner/TestThreadExecutionHelperTest.java | 81 +- .../RandomSwitchCTestAnnTest.java | 21 +- .../randomswitch/RandomSwitchOptionsTest.java | 20 +- .../strategy/stress/StressCTestAnnTest.java | 19 +- .../strategy/stress/StressOptionsTest.java | 29 +- .../test/verifier/CustomScenarioDSL.kt | 0 .../test/verifier/EpsilonVerifierTest.kt | 14 +- .../verifier/SequentialSpecificationTest.kt | 22 +- .../BufferedChannelCustomTest.kt | 10 +- .../linearizability/BufferedChannelTest.kt | 14 +- .../verifier/linearizability/ClocksTest.kt | 4 +- .../linearizability/ConcurrentDequeTest.kt | 22 +- .../ConcurrentHashMapTest.java | 18 +- .../ConcurrentLinkedQueueTest.kt | 14 +- .../verifier/linearizability/CounterTests.kt | 80 + .../verifier/linearizability/HashMapTest.kt | 42 + .../linearizability/LockBasedSetTests.kt | 126 ++ .../RendezvousChannelCustomTest.kt | 0 .../linearizability/RendezvousChannelTest.kt | 21 +- .../linearizability/SequentialIntChannel.kt | 118 ++ .../linearizability/SkipListMapTest.kt | 23 +- .../quiescent/LockFreeTaskQueueTest.kt | 57 + .../serializability/SerializableQueueTest.kt | 70 + 279 files changed, 1464 insertions(+), 27908 deletions(-) delete mode 100755 libtest/pom.xml delete mode 100644 libtest/src/main/java/com/github/lock/free/queue/BlockingQueueAdapter.java delete mode 100644 libtest/src/main/java/com/github/lock/free/queue/LockFreeQueue.java delete mode 100644 libtest/src/main/java/com/github/lock/free/queue/Node.java delete mode 100644 libtest/src/main/java/com/github/lock/free/queue/SimpleQueue.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/AbstractEntry.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/ConcurrentAutoTable.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/Counter.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/IntIterator.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMap.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMapLong.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashSet.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashtable.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingIdentityHashMap.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingSetInt.java delete mode 100644 libtest/src/main/java/org/cliffc/high_scale_lib/old/UtilUnsafe.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputBusy.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputYield.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueBenchmark.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueRoundTripLatency.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputBusy.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputYield.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedOffer.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedPoll.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArray1ReadWrite.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArray2ReadWrite.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArray3ReadWrite.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArray4ReadWrite.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue1.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue2.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue3.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue4.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/Pow2.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/common/SPSCQueueFactory.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue1.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue2.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue1.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue2.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue3.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue4.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue5.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/lamport/VolatileLongCell.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/thompson/LongCell.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue1.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue2.java delete mode 100644 libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue3.java delete mode 100644 libtest/src/main/java/romix/scala/None.java delete mode 100644 libtest/src/main/java/romix/scala/Option.java delete mode 100644 libtest/src/main/java/romix/scala/Some.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/BasicNode.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/CNodeBase.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/Gen.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/INodeBase.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/ListMap.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/MainNode.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/Pair.java delete mode 100644 libtest/src/main/java/romix/scala/collection/concurrent/TrieMap.java delete mode 100644 libtest/src/main/java/tests/custom/counter/Counter.java delete mode 100644 libtest/src/main/java/tests/custom/counter/CounterCorrect2.java delete mode 100644 libtest/src/main/java/tests/custom/counter/CounterGet.java delete mode 100644 libtest/src/main/java/tests/custom/counter/CounterWrong0.java delete mode 100644 libtest/src/main/java/tests/custom/counter/CounterWrong1.java delete mode 100644 libtest/src/main/java/tests/custom/counter/CounterWrong2.java delete mode 100644 libtest/src/main/java/tests/custom/queue/Queue.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueEmptyException.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueFullException.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueSynchronized.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueWrong1.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueWrong2.java delete mode 100644 libtest/src/main/java/tests/custom/queue/QueueWrong3.java delete mode 100644 libtest/src/main/java/tests/custom/transfer/Accounts.java delete mode 100644 libtest/src/main/java/tests/custom/transfer/AccountsWrong1.java delete mode 100644 libtest/src/main/java/tests/custom/transfer/AccountsWrong2.java delete mode 100644 libtest/src/main/java/tests/custom/transfer/AccountsWrong3.java delete mode 100644 libtest/src/main/java/tests/custom/transfer/AccountsWrong4.java delete mode 100644 libtest/src/main/java/thesis_example/Counter.java delete mode 100644 libtest/src/main/java/thesis_example/CounterCorrect1.java delete mode 100644 libtest/src/main/java/thesis_example/CounterCorrect2.java delete mode 100644 libtest/src/main/java/thesis_example/CounterWrong1.java delete mode 100644 libtest/src/main/java/thesis_example/CounterWrong2.java delete mode 100644 libtest/src/main/java/thesis_example/QueueCorrect.java delete mode 100644 libtest/src/main/java/z/annotation/InternalUse.java delete mode 100644 libtest/src/main/java/z/annotation/NotThreadSafe.java delete mode 100644 libtest/src/main/java/z/annotation/PerformanceDegraded.java delete mode 100644 libtest/src/main/java/z/annotation/ThreadSafe.java delete mode 100644 libtest/src/main/java/z/channel/BroadcastChannel.java delete mode 100644 libtest/src/main/java/z/channel/Channel.java delete mode 100644 libtest/src/main/java/z/channel/Channels.java delete mode 100644 libtest/src/main/java/z/channel/GenericHyperLoop.java delete mode 100644 libtest/src/main/java/z/channel/GenericMPMCQueue.java delete mode 100644 libtest/src/main/java/z/channel/IntHyperLoop.java delete mode 100644 libtest/src/main/java/z/channel/LongHyperLoop.java delete mode 100644 libtest/src/main/java/z/channel/MPMCQueue.java delete mode 100644 libtest/src/main/java/z/channel/ReceivePort.java delete mode 100644 libtest/src/main/java/z/evil/Intrinsics.java delete mode 100644 libtest/src/main/java/z/exception/ContractViolatedException.java delete mode 100644 libtest/src/main/java/z/function/ExceptionalFunction0.java delete mode 100644 libtest/src/main/java/z/function/ExceptionalRunnable.java delete mode 100644 libtest/src/main/java/z/function/Function0.java delete mode 100644 libtest/src/main/java/z/function/Function1.java delete mode 100644 libtest/src/main/java/z/function/Function10.java delete mode 100644 libtest/src/main/java/z/function/Function11.java delete mode 100644 libtest/src/main/java/z/function/Function2.java delete mode 100644 libtest/src/main/java/z/function/Function3.java delete mode 100644 libtest/src/main/java/z/function/Function4.java delete mode 100644 libtest/src/main/java/z/function/Function5.java delete mode 100644 libtest/src/main/java/z/function/Function6.java delete mode 100644 libtest/src/main/java/z/function/Function7.java delete mode 100644 libtest/src/main/java/z/function/Function8.java delete mode 100644 libtest/src/main/java/z/function/Function9.java delete mode 100644 libtest/src/main/java/z/function/Functions.java delete mode 100644 libtest/src/main/java/z/function/Pipe.java delete mode 100644 libtest/src/main/java/z/function/Pipeline.java delete mode 100644 libtest/src/main/java/z/function/ThrowableFunction0.java delete mode 100644 libtest/src/main/java/z/function/ThrowableRunnable.java delete mode 100644 libtest/src/main/java/z/function/ToBooleanFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToByteFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToCharFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToDoubleFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToFloatFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToIntFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToLongFunction0.java delete mode 100644 libtest/src/main/java/z/function/ToShortFunction0.java delete mode 100644 libtest/src/main/java/z/module/License.java delete mode 100644 libtest/src/main/java/z/module/Module.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/Buffer.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/BufferStateException.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/Buffers.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/ByteBuffer.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/LittleEndianOrderBuffer.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/NativeOrderBuffer.java delete mode 100644 libtest/src/main/java/z/offheap/buffer/NetworkOrderBuffer.java delete mode 100644 libtest/src/main/java/z/offheap/zmalloc/Allocator.java delete mode 100644 libtest/src/main/java/z/util/Contracts.java delete mode 100644 libtest/src/main/java/z/util/MethodHandles.java delete mode 100644 libtest/src/main/java/z/util/SystemProperty.java delete mode 100644 libtest/src/main/java/z/util/Throwables.java delete mode 100644 libtest/src/main/java/z/util/Unsafes.java delete mode 100644 libtest/src/main/java/z/util/concurrent/ThreadLocalPool.java delete mode 100644 libtest/src/main/java/z/util/concurrent/ThreadLocalValueHolder.java delete mode 100644 libtest/src/main/java/z/util/primitives/Bytes.java delete mode 100644 libtest/src/main/java/z/util/primitives/Chars.java delete mode 100644 libtest/src/main/java/z/util/primitives/Ints.java delete mode 100644 libtest/src/main/java/z/util/primitives/Longs.java delete mode 100644 libtest/src/main/java/z/util/primitives/Primitives.java delete mode 100644 libtest/src/main/java/z/util/primitives/Shorts.java delete mode 100644 libtest/src/main/java/z/util/primitives/UnsignedBytes.java delete mode 100644 libtest/src/main/java/z/util/primitives/UnsignedInts.java delete mode 100644 libtest/src/main/java/z/util/primitives/UnsignedLongs.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDeque.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDequeTest.kt delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/BitVectorCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/MapCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/SetCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterGetTest.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest2.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest3.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest4.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong2.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong3.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/transfer/AccountsTest.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/MultisetCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/SetCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/blocking_queue/BlockingQueueTest1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/hash_map/HashMapTest.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/MutexStressTest.kt delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/lockfreequeue/QueueCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/romix/TrieCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect1.java delete mode 100644 libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect2.java delete mode 100755 lincheck/pom.xml delete mode 100644 lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.java delete mode 100644 lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java delete mode 100644 lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java delete mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt delete mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueue.kt delete mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueueTest.java delete mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulation.kt delete mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt (98%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java (94%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/CTestStructure.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java (100%) create mode 100644 src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt rename libtest/src/main/java/psy/lob/saw/queues/common/UnsafeAccess.java => src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt (62%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/Options.java (99%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt (66%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/Result.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt (98%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java (90%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java (97%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/annotations/Param.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java (99%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt (92%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java (100%) create mode 100644 src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt (89%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java (80%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java (97%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java (100%) create mode 100644 src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java (88%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyHolder.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyTransformer.java (100%) create mode 100644 src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTest.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java (86%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchOptions.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java (58%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java (86%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.java (100%) create mode 100644 src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java (89%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt (99%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt (100%) rename {lincheck/src => src}/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt (100%) create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt (100%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java (100%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt (88%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt (73%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java (51%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/OperationsInAbstractClassTest.kt (100%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java (67%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java (91%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt (95%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt (100%) create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt (85%) rename {lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability => src/test/java/org/jetbrains/kotlinx/lincheck/test/runner}/ResumingFollowUpTest.kt (97%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java (56%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java (69%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java (71%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java (73%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java (67%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt (100%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt (78%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt (68%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt (94%) rename lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt => src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelTest.kt (83%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt (96%) rename libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/ConcurrentDequeStressTest.kt => src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt (61%) rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java (67%) rename lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentQueueStressTest.kt => src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentLinkedQueueTest.kt (67%) create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/LockBasedSetTests.kt rename {lincheck/src => src}/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelCustomTest.kt (100%) rename lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt => src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelTest.kt (74%) create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt rename lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentSkipListMapStressTest.kt => src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt (63%) create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeTaskQueueTest.kt create mode 100644 src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueTest.kt diff --git a/README.md b/README.md index 6b6ce89b1..3258767de 100755 --- a/README.md +++ b/README.md @@ -406,15 +406,15 @@ public class HashMapLinearizabilityTest extends VerifierState { ``` = Invalid execution results = Init part: -[put(1,2): null, put(4,6): null, get(5): null, put(3,-6): null, put(1,-8): 2] +[put(1, 2): null, put(4, 6): null, get(5): null, put(3, -6): null, put(1, -8): 2] Parallel part: -| get(4): 6 | put(2,1): null | -| get(2): 1 [1,0] | put(5,4): null [1,1] | -| put(5,-8): null [2,1] | get(3): 5 [5,2] | -| get(3): -6 [3,1] | get(4): 6 [5,3] | -| put(3,5): -6 [4,1] | put(1,-4): -8 [5,4] | +| get(4): 6 | put(2, 1): null | +| get(2): 1 [1,-] | put(5, 4): null [1,1] | +| put(5, -8): null [2,1] | get(3): 5 [5,2] | +| get(3): -6 [3,1] | get(4): 6 [5,3] | +| put(3, 5): -6 [4,1] | put(1, -4): -8 [5,4] | Post part: -[put(5,-8): 4, put(5,-2): -8, get(1): -4, put(2,-8): 1, get(1): -4] +[put(5, -8): 4, put(5, -2): -8, get(1): -4, put(2, -8): 1, get(1): -4] --- values in "[..]" brackets indicate the number of completed operations in each of the parallel threads seen at the beginning of the current operation diff --git a/libtest/pom.xml b/libtest/pom.xml deleted file mode 100755 index c7189c366..000000000 --- a/libtest/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - org.jetbrains.kotlinx - lincheck-all - 2.7-SNAPSHOT - - 4.0.0 - - libtest - - - true - - - - - org.jctools - jctools-core - 1.0 - - - - org.openjdk.jmh - jmh-core - 0.6 - - - org.openjdk.jmh - jmh-generator-annprocess - 0.6 - provided - - - - com.google.guava - guava - 18.0 - - - - com.boundary - high-scale-lib - 1.0.6 - - - - com.github.romix - java-concurrent-hash-trie-map - 0.2.23 - - - org.jetbrains.kotlinx - lincheck - ${project.version} - test - - - \ No newline at end of file diff --git a/libtest/src/main/java/com/github/lock/free/queue/BlockingQueueAdapter.java b/libtest/src/main/java/com/github/lock/free/queue/BlockingQueueAdapter.java deleted file mode 100644 index a1424e14c..000000000 --- a/libtest/src/main/java/com/github/lock/free/queue/BlockingQueueAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.lock.free.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -/** - */ -public class BlockingQueueAdapter implements SimpleQueue { - - private final BlockingQueue queue; - - public BlockingQueueAdapter() { - queue = new LinkedBlockingQueue(); - } - - @Override - public void add(T x) { - queue.add(x); - } - - @Override - public T takeOrNull() { - try { - return queue.take(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } -} diff --git a/libtest/src/main/java/com/github/lock/free/queue/LockFreeQueue.java b/libtest/src/main/java/com/github/lock/free/queue/LockFreeQueue.java deleted file mode 100644 index 17cd917f4..000000000 --- a/libtest/src/main/java/com/github/lock/free/queue/LockFreeQueue.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.github.lock.free.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Daneel Yaitskov - */ -public class LockFreeQueue implements SimpleQueue { - - // never empty - private final AtomicLong length = new AtomicLong(1L); - private final Node stub = new Node(null); - private final AtomicReference> head = new AtomicReference>(stub); - private final AtomicReference> tail = new AtomicReference>(stub); - - @Override - public void add(T x) { - addNode(new Node(x)); - length.incrementAndGet(); - } - - @Override - public T takeOrNull() { - while (true) { - long l = length.get(); - if (l == 1) { - return null; - } - if (length.compareAndSet(l, l - 1)) { - break; - } - } - while (true) { - Node r = head.get(); - if (r == null) { - throw new IllegalStateException("null head"); - } - if (r.next.get() == null) { - length.incrementAndGet(); - return null; - } - if (head.compareAndSet(r, r.next.get())) { - if (r == stub) { - stub.next.set(null); - addNode(stub); - } else { - return r.ref; - } - } - } - } - - private void addNode(Node n) { - Node t; - while (true) { - t = tail.get(); - if (tail.compareAndSet(t, n)) { - break; - } - } - if (t.next.compareAndSet(null, n)) { - return; - } - throw new IllegalStateException("bad tail next"); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/com/github/lock/free/queue/Node.java b/libtest/src/main/java/com/github/lock/free/queue/Node.java deleted file mode 100644 index 8e26650d6..000000000 --- a/libtest/src/main/java/com/github/lock/free/queue/Node.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.lock.free.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicReference; - -/** -*/ -class Node { - final AtomicReference> next = new AtomicReference>(); - final T ref; - Node(T ref) { - this.ref = ref; - } -} diff --git a/libtest/src/main/java/com/github/lock/free/queue/SimpleQueue.java b/libtest/src/main/java/com/github/lock/free/queue/SimpleQueue.java deleted file mode 100644 index 28b289808..000000000 --- a/libtest/src/main/java/com/github/lock/free/queue/SimpleQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.lock.free.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - */ -public interface SimpleQueue { - void add(T x); - - T takeOrNull(); -} diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/AbstractEntry.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/AbstractEntry.java deleted file mode 100644 index 8c3f3f938..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/AbstractEntry.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.util.*; - -/** - * A simple implementation of {@link Map.Entry}. - * Does not implement {@link Map.Entry.setValue}, that is done by users of the class. - * - * @since 1.5 - * @author Cliff Click - * @param the type of keys maintained by this map - * @param the type of mapped values - */ - -abstract class AbstractEntry implements Map.Entry { - /** Strongly typed key */ - protected final TypeK _key; - /** Strongly typed value */ - protected TypeV _val; - - public AbstractEntry(final TypeK key, final TypeV val) { _key = key; _val = val; } - public AbstractEntry(final Map.Entry e ) { _key = e.getKey(); _val = e.getValue(); } - /** Return "key=val" string */ - public String toString() { return _key + "=" + _val; } - /** Return key */ - public TypeK getKey () { return _key; } - /** Return val */ - public TypeV getValue() { return _val; } - - /** Equal if the underlying key & value are equal */ - public boolean equals(final Object o) { - if (!(o instanceof Map.Entry)) return false; - final Map.Entry e = (Map.Entry)o; - return eq(_key, e.getKey()) && eq(_val, e.getValue()); - } - - /** Compute "key.hashCode() ^ val.hashCode()" */ - public int hashCode() { - return - ((_key == null) ? 0 : _key.hashCode()) ^ - ((_val == null) ? 0 : _val.hashCode()); - } - - private static boolean eq(final Object o1, final Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); - } -} - diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/ConcurrentAutoTable.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/ConcurrentAutoTable.java deleted file mode 100644 index 8eae3f8eb..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/ConcurrentAutoTable.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.Serializable; -import java.util.concurrent.atomic.*; - -import sun.misc.Unsafe; - -/** - * An auto-resizing table of {@code longs}, supporting low-contention CAS - * operations. Updates are done with CAS's to no particular table element. - * The intent is to support highly scalable counters, random/w locks, and other - * structures where the updates are associative, loss-free (no-brainer), and - * otherwise happen at such a high volume that the cache contention for - * CAS'ing a single word is unacceptable. - * - *

This API is overkill for simple counters (e.g. no need for the 'mask') - * and is untested as an API for making a scalable random/w lock and so is likely - * to change! - * - * @since 1.5 - * @author Cliff Click - */ - - -public class ConcurrentAutoTable implements Serializable { - - // --- public interface --- - - /** - * Add the given value to current counter value. Concurrent updates will - * not be lost, but addAndGet or getAndAdd are not implemented because the - * total counter value (i.e., {@link #get}) is not atomically updated. - * Updates are striped across an array of counters to avoid cache contention - * and has been tested with performance scaling linearly up to 768 CPUs. - */ - public void add( long x ) { add_if_mask( x,0); } - /** {@link #add} with -1 */ - public void decrement() { add_if_mask(-1L,0); } - /** {@link #add} with +1 */ - public void increment() { add_if_mask( 1L,0); } - - /** Atomically set the sum of the striped counters to specified value. - * Rather more expensive than a simple store, in order to remain atomic. - */ - public void set( long x ) { - CAT newcat = new CAT(null,4,x); - // Spin until CAS works - while( !CAS_cat(_cat,newcat) ); - } - - /** - * Current value of the counter. Since other threads are updating furiously - * the value is only approximate, but it includes all counts made by the - * current thread. Requires a pass over the internally striped counters. - */ - public long get() { return _cat.sum(0); } - /** Same as {@link #get}, included for completeness. */ - public int intValue() { return (int)_cat.sum(0); } - /** Same as {@link #get}, included for completeness. */ - public long longValue() { return _cat.sum(0); } - - /** - * A cheaper {@link #get}. Updated only once/millisecond, but as fast as a - * simple load instruction when not updating. - */ - public long estimate_get( ) { return _cat.estimate_sum(0); } - - /** - * Return the counter's {@code long} value converted to a string. - */ - public String toString() { return _cat.toString(0); } - - /** - * A more verbose print than {@link #toString}, showing internal structure. - * Useful for debugging. - */ - public void print() { _cat.print(); } - - /** - * Return the internal counter striping factor. Useful for diagnosing - * performance problems. - */ - public int internal_size() { return _cat._t.length; } - - // Only add 'x' to some slot in table, hinted at by 'hash', if bits under - // the mask are all zero. The sum can overflow or 'x' can contain bits in - // the mask. Value is CAS'd so no counts are lost. The CAS is retried until - // it succeeds or bits are found under the mask. Returned value is the old - // value - which WILL have zero under the mask on success and WILL NOT have - // zero under the mask for failure. - private long add_if_mask( long x, long mask ) { return _cat.add_if_mask(x,mask,hash(),this); } - - // The underlying array of concurrently updated long counters - private volatile CAT _cat = new CAT(null,4/*Start Small, Think Big!*/,0L); - private static final AtomicReferenceFieldUpdater _catUpdater = - AtomicReferenceFieldUpdater.newUpdater(ConcurrentAutoTable.class,CAT.class, "_cat"); - private boolean CAS_cat( CAT oldcat, CAT newcat ) { return _catUpdater.compareAndSet(this,oldcat,newcat); } - - // Hash spreader - private static final int hash() { - int h = System.identityHashCode(Thread.currentThread()); - // You would think that System.identityHashCode on the current thread - // would be a good hash fcn, but actually on SunOS 5.8 it is pretty lousy - // in the low bits. - h ^= (h>>>20) ^ (h>>>12); // Bit spreader, borrowed from Doug Lea - h ^= (h>>> 7) ^ (h>>> 4); - return h<<2; // Pad out cache lines. The goal is to avoid cache-line contention - } - - // --- CAT ----------------------------------------------------------------- - private static class CAT implements Serializable { - - // Unsafe crud: get a function which will CAS arrays - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - private static final int _Lbase = _unsafe.arrayBaseOffset(long[].class); - private static final int _Lscale = _unsafe.arrayIndexScale(long[].class); - private static long rawIndex(long[] ary, int i) { - assert i >= 0 && i < ary.length; - return _Lbase + i * _Lscale; - } - private final static boolean CAS( long[] A, int idx, long old, long nnn ) { - return _unsafe.compareAndSwapLong( A, rawIndex(A,idx), old, nnn ); - } - - volatile long _resizers; // count of threads attempting a resize - static private final AtomicLongFieldUpdater _resizerUpdater = - AtomicLongFieldUpdater.newUpdater(CAT.class, "_resizers"); - - private final CAT _next; - private volatile long _sum_cache; - private volatile long _fuzzy_sum_cache; - private volatile long _fuzzy_time; - private static final int MAX_SPIN=2; - private long[] _t; // Power-of-2 array of longs - - CAT( CAT next, int sz, long init ) { - _next = next; - _sum_cache = Long.MIN_VALUE; - _t = new long[sz]; - _t[0] = init; - } - - // Only add 'x' to some slot in table, hinted at by 'hash', if bits under - // the mask are all zero. The sum can overflow or 'x' can contain bits in - // the mask. Value is CAS'd so no counts are lost. The CAS is attempted - // ONCE. - public long add_if_mask( long x, long mask, int hash, ConcurrentAutoTable master ) { - long[] t = _t; - int idx = hash & (t.length-1); - // Peel loop; try once fast - long old = t[idx]; - boolean ok = CAS( t, idx, old&~mask, old+x ); - if( _sum_cache != Long.MIN_VALUE ) - _sum_cache = Long.MIN_VALUE; // Blow out cache - if( ok ) return old; // Got it - if( (old&mask) != 0 ) return old; // Failed for bit-set under mask - // Try harder - int cnt=0; - while( true ) { - old = t[idx]; - if( (old&mask) != 0 ) return old; // Failed for bit-set under mask - if( CAS( t, idx, old, old+x ) ) break; // Got it! - cnt++; - } - if( cnt < MAX_SPIN ) return old; // Allowable spin loop count - if( t.length >= 1024*1024 ) return old; // too big already - - // Too much contention; double array size in an effort to reduce contention - long r = _resizers; - int newbytes = (t.length<<1)<<3/*word to bytes*/; - while( !_resizerUpdater.compareAndSet(this,r,r+newbytes) ) - r = _resizers; - r += newbytes; - if( master._cat != this ) return old; // Already doubled, don't bother - if( (r>>17) != 0 ) { // Already too much allocation attempts? - // TODO - use a wait with timeout, so we'll wakeup as soon as the new - // table is ready, or after the timeout in any case. Annoyingly, this - // breaks the non-blocking property - so for now we just briefly sleep. - //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup - try { Thread.sleep(r>>17); } catch( InterruptedException e ) { } - if( master._cat != this ) return old; - } - - CAT newcat = new CAT(this,t.length*2,0); - // Take 1 stab at updating the CAT with the new larger size. If this - // fails, we assume some other thread already expanded the CAT - so we - // do not need to retry until it succeeds. - master.CAS_cat(this,newcat); - return old; - } - - - // Return the current sum of all things in the table, stripping off mask - // before the add. Writers can be updating the table furiously, so the - // sum is only locally accurate. - public long sum( long mask ) { - long sum = _sum_cache; - if( sum != Long.MIN_VALUE ) return sum; - sum = _next == null ? 0 : _next.sum(mask); // Recursively get cached sum - long[] t = _t; - for( int i=0; i. - * #L% - */ - -/** - * A simple high-performance counter. Merely renames the extended {@link - * ConcurrentAutoTable} class to be more obvious. - * {@link ConcurrentAutoTable} already has a decent - * counting API. - * - * @since 1.5 - * @author Cliff Click - */ - -public class Counter extends ConcurrentAutoTable { - - // Add the given value to current counter value. Concurrent updates will - // not be lost, but addAndGet or getAndAdd are not implemented because but - // the total counter value is not atomically updated. - //public void add( long x ); - //public void decrement(); - //public void increment(); - - // Current value of the counter. Since other threads are updating furiously - // the value is only approximate, but it includes all counts made by the - // current thread. Requires a pass over all the striped counters. - //public long get(); - //public int intValue(); - //public long longValue(); - - // A cheaper 'get'. Updated only once/millisecond, but fast as a simple - // load instruction when not updating. - //public long estimate_get( ); - -} - diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/IntIterator.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/IntIterator.java deleted file mode 100644 index 1ab7c4915..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/IntIterator.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public interface IntIterator { - public int next(); - public boolean hasNext(); -} diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMap.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMap.java deleted file mode 100644 index 460b4b730..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMap.java +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.*; - -import sun.misc.Unsafe; - -/** - * A lock-free alternate implementation of {@link java.util.concurrent.ConcurrentHashMap} - * with better scaling properties and generally lower costs to mutate the Map. - * It provides identical correctness properties as ConcurrentHashMap. All - * operations are non-blocking and multi-thread safe, including all update - * operations. {@link NonBlockingHashMap} scales substatially better than - * {@link java.util.concurrent.ConcurrentHashMap} for high update rates, even with a - * large concurrency factor. Scaling is linear up to 768 CPUs on a 768-CPU - * Azul box, even with 100% updates or 100% reads or any fraction in-between. - * Linear scaling up to all cpus has been observed on a 32-way Sun US2 box, - * 32-way Sun Niagra box, 8-way Intel box and a 4-way Power box. - * - * This class obeys the same functional specification as {@link - * Hashtable}, and includes versions of methods corresponding to - * each method of Hashtable. However, even though all operations are - * thread-safe, operations do not entail locking and there is - * not any support for locking the entire table in a way that - * prevents all access. This class is fully interoperable with - * Hashtable in programs that rely on its thread safety but not on - * its synchronization details. - * - *

Operations (including put) generally do not block, so may - * overlap with other update operations (including other puts and - * removes). Retrievals reflect the results of the most recently - * completed update operations holding upon their onset. For - * aggregate operations such as putAll, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, Iterators - * and Enumerations return elements reflecting the state of the hash table at - * some point at or since the creation of the iterator/enumeration. They do - * not throw {@link ConcurrentModificationException}. However, - * iterators are designed to be used by only one thread at a time. - * - *

Very full tables, or tables with high reprobe rates may trigger an - * internal resize operation to move into a larger table. Resizing is not - * terribly expensive, but it is not free either; during resize operations - * table throughput may drop somewhat. All threads that visit the table - * during a resize will 'help' the resizing but will still be allowed to - * complete their operation before the resize is finished (i.e., a simple - * 'get' operation on a million-entry table undergoing resizing will not need - * to block until the entire million entries are copied). - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow null to be used as a key or value. - * - * - * @since 1.5 - * @author Cliff Click - * @param the type of keys maintained by this map - * @param the type of mapped values - * - * @version 1.1.2 - * @author Prashant Deva - moved hash() function out of get_impl() so it is - * not calculated multiple times. - */ - -public class NonBlockingHashMap - extends AbstractMap - implements ConcurrentMap, Cloneable, Serializable { - - private static final long serialVersionUID = 1234123412341234123L; - - private static final int REPROBE_LIMIT=10; // Too many reprobes then force a table-resize - - // --- Bits to allow Unsafe access to arrays - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - private static final int _Obase = _unsafe.arrayBaseOffset(Object[].class); - private static final int _Oscale = _unsafe.arrayIndexScale(Object[].class); - private static long rawIndex(final Object[] ary, final int idx) { - assert idx >= 0 && idx < ary.length; - return _Obase + idx * _Oscale; - } - - // --- Setup to use Unsafe - private static final long _kvs_offset; - static { // - Field f = null; - try { f = NonBlockingHashMap.class.getDeclaredField("_kvs"); } - catch( NoSuchFieldException e ) { throw new RuntimeException(e); } - _kvs_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS_kvs( final Object[] oldkvs, final Object[] newkvs ) { - return _unsafe.compareAndSwapObject(this, _kvs_offset, oldkvs, newkvs ); - } - - // --- Adding a 'prime' bit onto Values via wrapping with a junk wrapper class - private static final class Prime { - final Object _V; - Prime( Object V ) { _V = V; } - static Object unbox( Object V ) { return V instanceof Prime ? ((Prime)V)._V : V; } - } - - // --- hash ---------------------------------------------------------------- - // Helper function to spread lousy hashCodes - private static final int hash(final Object key) { - int h = key.hashCode(); // The real hashCode call - // Spread bits to regularize both segment and index locations, - // using variant of single-word Wang/Jenkins hash. - h += (h << 15) ^ 0xffffcd7d; - h ^= (h >>> 10); - h += (h << 3); - h ^= (h >>> 6); - h += (h << 2) + (h << 14); - return h ^ (h >>> 16); - } - - // --- The Hash Table -------------------- - // Slot 0 is always used for a 'CHM' entry below to hold the interesting - // bits of the hash table. Slot 1 holds full hashes as an array of ints. - // Slots {2,3}, {4,5}, etc hold {Key,Value} pairs. The entire hash table - // can be atomically replaced by CASing the _kvs field. - // - // Why is CHM buried inside the _kvs Object array, instead of the other way - // around? The CHM info is used during resize events and updates, but not - // during standard 'get' operations. I assume 'get' is much more frequent - // than 'put'. 'get' can skip the extra indirection of skipping through the - // CHM to reach the _kvs array. - private transient Object[] _kvs; - private static final CHM chm (Object[] kvs) { return (CHM )kvs[0]; } - private static final int[] hashes(Object[] kvs) { return (int[])kvs[1]; } - // Number of K,V pairs in the table - private static final int len(Object[] kvs) { return (kvs.length-2)>>1; } - - // Time since last resize - private transient long _last_resize_milli; - - // --- Minimum table size ---------------- - // Pick size 8 K/V pairs, which turns into (8*2+2)*4+12 = 84 bytes on a - // standard 32-bit HotSpot, and (8*2+2)*8+12 = 156 bytes on 64-bit Azul. - private static final int MIN_SIZE_LOG=3; // - private static final int MIN_SIZE=(1<>2); - } - - // --- NonBlockingHashMap -------------------------------------------------- - // Constructors - - /** Create a new NonBlockingHashMap with default minimum size (currently set - * to 8 K/V pairs or roughly 84 bytes on a standard 32-bit JVM). */ - public NonBlockingHashMap( ) { this(MIN_SIZE); } - - /** Create a new NonBlockingHashMap with initial room for the given number of - * elements, thus avoiding internal resizing operations to reach an - * appropriate size. Large numbers here when used with a small count of - * elements will sacrifice space for a small amount of time gained. The - * initial size will be rounded up internally to the next larger power of 2. */ - public NonBlockingHashMap( final int initial_sz ) { initialize(initial_sz); } - private final void initialize( int initial_sz ) { - if( initial_sz < 0 ) throw new IllegalArgumentException(); - int i; // Convert to next largest power-of-2 - if( initial_sz > 1024*1024 ) initial_sz = 1024*1024; - for( i=MIN_SIZE_LOG; (1<size() == 0. - * @return size() == 0 */ - @Override - public boolean isEmpty ( ) { return size() == 0; } - - /** Tests if the key in the table using the equals method. - * @return true if the key is in the table using the equals method - * @throws NullPointerException if the specified key is null */ - @Override - public boolean containsKey( Object key ) { return get(key) != null; } - - /** Legacy method testing if some key maps into the specified value in this - * table. This method is identical in functionality to {@link - * #containsValue}, and exists solely to ensure full compatibility with - * class {@link Hashtable}, which supported this method prior to - * introduction of the Java Collections framework. - * @param val a value to search for - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean contains ( Object val ) { return containsValue(val); } - - /** Maps the specified key to the specified value in the table. Neither key - * nor value can be null. - *

The value can be retrieved by calling {@link #get} with a key that is - * equal to the original key. - * @param key key with which the specified value is to be associated - * @param val value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key or value is null */ - @Override - public TypeV put ( TypeK key, TypeV val ) { return putIfMatch( key, val, NO_MATCH_OLD); } - - /** Atomically, do a {@link #put} if-and-only-if the key is not mapped. - * Useful to ensure that only a single mapping for the key exists, even if - * many threads are trying to create the mapping in parallel. - * @return the previous value associated with the specified key, - * or null if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null */ - public TypeV putIfAbsent( TypeK key, TypeV val ) { return putIfMatch( key, val, TOMBSTONE ); } - - /** Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key is null */ - @Override - public TypeV remove ( Object key ) { return putIfMatch( key,TOMBSTONE, NO_MATCH_OLD); } - - /** Atomically do a {@link #remove(Object)} if-and-only-if the key is mapped - * to a value which is equals to the given value. - * @throws NullPointerException if the specified key or value is null */ - public boolean remove ( Object key,Object val ) { - final Object out = putIfMatch(key, TOMBSTONE, val); - return (val == null) ? out == val : val.equals(out); - } - - /** Atomically do a put(key,val) if-and-only-if the key is - * mapped to some value already. - * @throws NullPointerException if the specified key or value is null */ - public TypeV replace ( TypeK key, TypeV val ) { return putIfMatch( key, val,MATCH_ANY ); } - - /** Atomically do a put(key,newValue) if-and-only-if the key is - * mapped a value which is equals to oldValue. - * @throws NullPointerException if the specified key or value is null */ - public boolean replace ( TypeK key, TypeV oldValue, TypeV newValue ) { - final Object out = putIfMatch(key, newValue, oldValue); - return (oldValue == null) ? out == oldValue : oldValue.equals(out); - } - - private final TypeV putIfMatch( Object key, Object newVal, Object oldVal ) { - if (oldVal == null || newVal == null) throw new NullPointerException(); - final Object res = putIfMatch( this, _kvs, key, newVal, oldVal ); - assert !(res instanceof Prime); - assert res != null; - return res == TOMBSTONE ? null : (TypeV)res; - } - - - /** Copies all of the mappings from the specified map to this one, replacing - * any existing mappings. - * @param m mappings to be stored in this map */ - @Override - public void putAll(Map m) { - for (Entry e : m.entrySet()) - put(e.getKey(), e.getValue()); - } - - /** Removes all of the mappings from this map. */ - @Override - public void clear() { // Smack a new empty table down - Object[] newkvs = new NonBlockingHashMap(MIN_SIZE)._kvs; - while( !CAS_kvs(_kvs,newkvs) ) // Spin until the clear works - ; - } - - /** Returns true if this Map maps one or more keys to the specified - * value. Note: This method requires a full internal traversal of the - * hash table and is much slower than {@link #containsKey}. - * @param val value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - @Override - public boolean containsValue( final Object val ) { - if( val == null ) throw new NullPointerException(); - for( TypeV V : values() ) - if( V == val || V.equals(val) ) - return true; - return false; - } - - // This function is supposed to do something for Hashtable, and the JCK - // tests hang until it gets called... by somebody ... for some reason, - // any reason.... - protected void rehash() { - } - - /** - * Creates a shallow copy of this hashtable. All the structure of the - * hashtable itself is copied, but the keys and values are not cloned. - * This is a relatively expensive operation. - * - * @return a clone of the hashtable. - */ - @Override - public Object clone() { - try { - // Must clone, to get the class right; NBHM might have been - // extended so it would be wrong to just make a new NBHM. - NonBlockingHashMap t = (NonBlockingHashMap) super.clone(); - // But I don't have an atomic clone operation - the underlying _kvs - // structure is undergoing rapid change. If I just clone the _kvs - // field, the CHM in _kvs[0] won't be in sync. - // - // Wipe out the cloned array (it was shallow anyways). - t.clear(); - // Now copy sanely - for( TypeK K : keySet() ) { - final TypeV V = get(K); // Do an official 'get' - t.put(K,V); - } - return t; - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Returns a string representation of this map. The string representation - * consists of a list of key-value mappings in the order returned by the - * map's entrySet view's iterator, enclosed in braces - * ("{}"). Adjacent mappings are separated by the characters - * ", " (comma and space). Each key-value mapping is rendered as - * the key followed by an equals sign ("=") followed by the - * associated value. Keys and values are converted to strings as by - * {@link String#valueOf(Object)}. - * - * @return a string representation of this map - */ - @Override - public String toString() { - Iterator> i = entrySet().iterator(); - if( !i.hasNext()) - return "{}"; - - StringBuilder sb = new StringBuilder(); - sb.append('{'); - for (;;) { - Entry e = i.next(); - TypeK key = e.getKey(); - TypeV value = e.getValue(); - sb.append(key == this ? "(this Map)" : key); - sb.append('='); - sb.append(value == this ? "(this Map)" : value); - if( !i.hasNext()) - return sb.append('}').toString(); - sb.append(", "); - } - } - - // --- keyeq --------------------------------------------------------------- - // Check for key equality. Try direct pointer compare first, then see if - // the hashes are unequal (fast negative test) and finally do the full-on - // 'equals' v-call. - private static boolean keyeq( Object K, Object key, int[] hashes, int hash, int fullhash ) { - return - K==key || // Either keys match exactly OR - // hash exists and matches? hash can be zero during the install of a - // new key/value pair. - ((hashes[hash] == 0 || hashes[hash] == fullhash) && - // Do not call the users' "equals()" call with a Tombstone, as this can - // surprise poorly written "equals()" calls that throw exceptions - // instead of simply returning false. - K != TOMBSTONE && // Do not call users' equals call with a Tombstone - // Do the match the hard way - with the users' key being the loop- - // invariant "this" pointer. I could have flipped the order of - // operands (since equals is commutative), but I'm making mega-morphic - // v-calls in a reprobing loop and nailing down the 'this' argument - // gives both the JIT and the hardware a chance to prefetch the call target. - key.equals(K)); // Finally do the hard match - } - - // --- get ----------------------------------------------------------------- - /** Returns the value to which the specified key is mapped, or {@code null} - * if this map contains no mapping for the key. - *

More formally, if this map contains a mapping from a key {@code k} to - * a value {@code v} such that {@code key.equals(k)}, then this method - * returns {@code v}; otherwise it returns {@code null}. (There can be at - * most one such mapping.) - * @throws NullPointerException if the specified key is null */ - // Never returns a Prime nor a Tombstone. - @Override - public TypeV get( Object key ) { - final int fullhash= hash (key); // throws NullPointerException if key is null - final Object V = get_impl(this,_kvs,key,fullhash); - assert !(V instanceof Prime); // Never return a Prime - return (TypeV)V; - } - - private static final Object get_impl( final NonBlockingHashMap topmap, final Object[] kvs, final Object key, final int fullhash ) { - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // The CHM, for a volatile read below; reads slot 0 of kvs - final int[] hashes=hashes(kvs); // The memoized hashes; reads slot 1 of kvs - - int idx = fullhash & (len-1); // First key hash - - // Main spin/reprobe loop, looking for a Key hit - int reprobe_cnt=0; - while( true ) { - // Probe table. Each read of 'val' probably misses in cache in a big - // table; hopefully the read of 'key' then hits in cache. - final Object K = key(kvs,idx); // Get key before volatile read, could be null - final Object V = val(kvs,idx); // Get value before volatile read, could be null or Tombstone or Prime - if( K == null ) return null; // A clear miss - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - // . - // We also need a volatile-read between reading a newly inserted Value - // and returning the Value (so the user might end up reading the stale - // Value contents). Same problem as with keys - and the one volatile - // read covers both. - final Object[] newkvs = chm._newkvs; // VOLATILE READ before key compare - - // Key-compare - if( keyeq(K,key,hashes,idx,fullhash) ) { - // Key hit! Check for no table-copy-in-progress - if( !(V instanceof Prime) ) // No copy? - return (V == TOMBSTONE) ? null : V; // Return the value - // Key hit - but slot is (possibly partially) copied to the new table. - // Finish the copy & retry in the new table. - return get_impl(topmap,chm.copy_slot_and_check(topmap,kvs,idx,key),key,fullhash); // Retry in the new table - } - // get and put must have the same key lookup logic! But only 'put' - // needs to force a table-resize for a too-long key-reprobe sequence. - // Check for too-many-reprobes on get - and flip to the new table. - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes - key == TOMBSTONE ) // found a TOMBSTONE key, means no more keys in this table - return newkvs == null ? null : get_impl(topmap,topmap.help_copy(newkvs),key,fullhash); // Retry in the new table - - idx = (idx+1)&(len-1); // Reprobe by 1! (could now prefetch) - } - } - - // --- putIfMatch --------------------------------------------------------- - // Put, Remove, PutIfAbsent, etc. Return the old value. If the returned - // value is equal to expVal (or expVal is NO_MATCH_OLD) then the put can be - // assumed to work (although might have been immediately overwritten). Only - // the path through copy_slot passes in an expected value of null, and - // putIfMatch only returns a null if passed in an expected null. - private static final Object putIfMatch( final NonBlockingHashMap topmap, final Object[] kvs, final Object key, final Object putval, final Object expVal ) { - assert putval != null; - assert !(putval instanceof Prime); - assert !(expVal instanceof Prime); - final int fullhash = hash (key); // throws NullPointerException if key null - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // Reads kvs[0] - final int[] hashes = hashes(kvs); // Reads kvs[1], read before kvs[0] - int idx = fullhash & (len-1); - - // --- - // Key-Claim stanza: spin till we can claim a Key (or force a resizing). - int reprobe_cnt=0; - Object K=null, V=null; - Object[] newkvs=null; - while( true ) { // Spin till we get a Key slot - V = val(kvs,idx); // Get old value (before volatile read below!) - K = key(kvs,idx); // Get current key - if( K == null ) { // Slot is free? - // Found an empty Key slot - which means this Key has never been in - // this table. No need to put a Tombstone - the Key is not here! - if( putval == TOMBSTONE ) return putval; // Not-now & never-been in this table - // Claim the null key-slot - if( CAS_key(kvs,idx, null, key ) ) { // Claim slot for Key - chm._slots.add(1); // Raise key-slots-used count - hashes[idx] = fullhash; // Memoize fullhash - break; // Got it! - } - // CAS to claim the key-slot failed. - // - // This re-read of the Key points out an annoying short-coming of Java - // CAS. Most hardware CAS's report back the existing value - so that - // if you fail you have a *witness* - the value which caused the CAS - // to fail. The Java API turns this into a boolean destroying the - // witness. Re-reading does not recover the witness because another - // thread can write over the memory after the CAS. Hence we can be in - // the unfortunate situation of having a CAS fail *for cause* but - // having that cause removed by a later store. This turns a - // non-spurious-failure CAS (such as Azul has) into one that can - // apparently spuriously fail - and we avoid apparent spurious failure - // by not allowing Keys to ever change. - K = key(kvs,idx); // CAS failed, get updated value - assert K != null; // If keys[idx] is null, CAS shoulda worked - } - // Key slot was not null, there exists a Key here - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - newkvs = chm._newkvs; // VOLATILE READ before key compare - - if( keyeq(K,key,hashes,idx,fullhash) ) - break; // Got it! - - // get and put must have the same key lookup logic! Lest 'get' give - // up looking too soon. - //topmap._reprobes.add(1); - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes or - key == TOMBSTONE ) { // found a TOMBSTONE key, means no more keys - // We simply must have a new table to do a 'put'. At this point a - // 'get' will also go to the new table (if any). We do not need - // to claim a key slot (indeed, we cannot find a free one to claim!). - newkvs = chm.resize(topmap,kvs); - if( expVal != null ) topmap.help_copy(newkvs); // help along an existing copy - return putIfMatch(topmap,newkvs,key,putval,expVal); - } - - idx = (idx+1)&(len-1); // Reprobe! - } // End of spinning till we get a Key slot - - // --- - // Found the proper Key slot, now update the matching Value slot. We - // never put a null, so Value slots monotonically move from null to - // not-null (deleted Values use Tombstone). Thus if 'V' is null we - // fail this fast cutout and fall into the check for table-full. - if( putval == V ) return V; // Fast cutout for no-change - - // See if we want to move to a new table (to avoid high average re-probe - // counts). We only check on the initial set of a Value from null to - // not-null (i.e., once per key-insert). Of course we got a 'free' check - // of newkvs once per key-compare (not really free, but paid-for by the - // time we get here). - if( newkvs == null && // New table-copy already spotted? - // Once per fresh key-insert check the hard way - ((V == null && chm.tableFull(reprobe_cnt,len)) || - // Or we found a Prime, but the JMM allowed reordering such that we - // did not spot the new table (very rare race here: the writing - // thread did a CAS of _newkvs then a store of a Prime. This thread - // reads the Prime, then reads _newkvs - but the read of Prime was so - // delayed (or the read of _newkvs was so accelerated) that they - // swapped and we still read a null _newkvs. The resize call below - // will do a CAS on _newkvs forcing the read. - V instanceof Prime) ) - newkvs = chm.resize(topmap,kvs); // Force the new table copy to start - // See if we are moving to a new table. - // If so, copy our slot and retry in the new table. - if( newkvs != null ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - - // --- - // We are finally prepared to update the existing table - while( true ) { - assert !(V instanceof Prime); - - // Must match old, and we do not? Then bail out now. Note that either V - // or expVal might be TOMBSTONE. Also V can be null, if we've never - // inserted a value before. expVal can be null if we are called from - // copy_slot. - - if( expVal != NO_MATCH_OLD && // Do we care about expected-Value at all? - V != expVal && // No instant match already? - (expVal != MATCH_ANY || V == TOMBSTONE || V == null) && - !(V==null && expVal == TOMBSTONE) && // Match on null/TOMBSTONE combo - (expVal == null || !expVal.equals(V)) ) // Expensive equals check at the last - return V; // Do not update! - - // Actually change the Value in the Key,Value pair - if( CAS_val(kvs, idx, V, putval ) ) { - // CAS succeeded - we did the update! - // Both normal put's and table-copy calls putIfMatch, but table-copy - // does not (effectively) increase the number of live k/v pairs. - if( expVal != null ) { - // Adjust sizes - a striped counter - if( (V == null || V == TOMBSTONE) && putval != TOMBSTONE ) chm._size.add( 1); - if( !(V == null || V == TOMBSTONE) && putval == TOMBSTONE ) chm._size.add(-1); - } - return (V==null && expVal!=null) ? TOMBSTONE : V; - } - // Else CAS failed - V = val(kvs,idx); // Get new value - // If a Prime'd value got installed, we need to re-run the put on the - // new table. Otherwise we lost the CAS to another racing put. - // Simply retry from the start. - if( V instanceof Prime ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - } - } - - // --- help_copy --------------------------------------------------------- - // Help along an existing resize operation. This is just a fast cut-out - // wrapper, to encourage inlining for the fast no-copy-in-progress case. We - // always help the top-most table copy, even if there are nested table - // copies in progress. - private final Object[] help_copy( Object[] helper ) { - // Read the top-level KVS only once. We'll try to help this copy along, - // even if it gets promoted out from under us (i.e., the copy completes - // and another KVS becomes the top-level copy). - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) return helper; // No copy in-progress - topchm.help_copy_impl(this,topkvs,false); - return helper; - } - - - // --- CHM ----------------------------------------------------------------- - // The control structure for the NonBlockingHashMap - private static final class CHM { - // Size in active K,V pairs - private final Counter _size; - public int size () { return (int)_size.get(); } - - // --- - // These next 2 fields are used in the resizing heuristics, to judge when - // it is time to resize or copy the table. Slots is a count of used-up - // key slots, and when it nears a large fraction of the table we probably - // end up reprobing too much. Last-resize-milli is the time since the - // last resize; if we are running back-to-back resizes without growing - // (because there are only a few live keys but many slots full of dead - // keys) then we need a larger table to cut down on the churn. - - // Count of used slots, to tell when table is full of dead unusable slots - private final Counter _slots; - public int slots() { return (int)_slots.get(); } - - // --- - // New mappings, used during resizing. - // The 'new KVs' array - created during a resize operation. This - // represents the new table being copied from the old one. It's the - // volatile variable that is read as we cross from one table to the next, - // to get the required memory orderings. It monotonically transits from - // null to set (once). - volatile Object[] _newkvs; - private final AtomicReferenceFieldUpdater _newkvsUpdater = - AtomicReferenceFieldUpdater.newUpdater(CHM.class,Object[].class, "_newkvs"); - // Set the _next field if we can. - boolean CAS_newkvs( Object[] newkvs ) { - while( _newkvs == null ) - if( _newkvsUpdater.compareAndSet(this,null,newkvs) ) - return true; - return false; - } - // Sometimes many threads race to create a new very large table. Only 1 - // wins the race, but the losers all allocate a junk large table with - // hefty allocation costs. Attempt to control the overkill here by - // throttling attempts to create a new table. I cannot really block here - // (lest I lose the non-blocking property) but late-arriving threads can - // give the initial resizing thread a little time to allocate the initial - // new table. The Right Long Term Fix here is to use array-lets and - // incrementally create the new very large array. In C I'd make the array - // with malloc (which would mmap under the hood) which would only eat - // virtual-address and not real memory - and after Somebody wins then we - // could in parallel initialize the array. Java does not allow - // un-initialized array creation (especially of ref arrays!). - volatile long _resizers; // count of threads attempting an initial resize - private static final AtomicLongFieldUpdater _resizerUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_resizers"); - - // --- - // Simple constructor - CHM( Counter size ) { - _size = size; - _slots= new Counter(); - } - - // --- tableFull --------------------------------------------------------- - // Heuristic to decide if this table is too full, and we should start a - // new table. Note that if a 'get' call has reprobed too many times and - // decided the table must be full, then always the estimate_sum must be - // high and we must report the table is full. If we do not, then we might - // end up deciding that the table is not full and inserting into the - // current table, while a 'get' has decided the same key cannot be in this - // table because of too many reprobes. The invariant is: - // slots.estimate_sum >= max_reprobe_cnt >= reprobe_limit(len) - private final boolean tableFull( int reprobe_cnt, int len ) { - return - // Do the cheap check first: we allow some number of reprobes always - reprobe_cnt >= REPROBE_LIMIT && - // More expensive check: see if the table is > 1/4 full. - _slots.estimate_get() >= reprobe_limit(len); - } - - // --- resize ------------------------------------------------------------ - // Resizing after too many probes. "How Big???" heuristics are here. - // Callers will (not this routine) will 'help_copy' any in-progress copy. - // Since this routine has a fast cutout for copy-already-started, callers - // MUST 'help_copy' lest we have a path which forever runs through - // 'resize' only to discover a copy-in-progress which never progresses. - private final Object[] resize( NonBlockingHashMap topmap, Object[] kvs) { - assert chm(kvs) == this; - - // Check for resize already in progress, probably triggered by another thread - Object[] newkvs = _newkvs; // VOLATILE READ - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // No copy in-progress, so start one. First up: compute new table size. - int oldlen = len(kvs); // Old count of K,V pairs allowed - int sz = size(); // Get current table count of active K,V pairs - int newsz = sz; // First size estimate - - // Heuristic to determine new size. We expect plenty of dead-slots-with-keys - // and we need some decent padding to avoid endless reprobing. - if( sz >= (oldlen>>2) ) { // If we are >25% full of keys then... - newsz = oldlen<<1; // Double size - if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - newsz = oldlen<<2; // Double double size - } - // This heuristic in the next 2 lines leads to a much denser table - // with a higher reprobe rate - //if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - // newsz = oldlen<<1; // Double size - - // Last (re)size operation was very recent? Then double again; slows - // down resize operations for tables subject to a high key churn rate. - long tm = System.currentTimeMillis(); - long q=0; - if( newsz <= oldlen && // New table would shrink or hold steady? - tm <= topmap._last_resize_milli+10000 && // Recent resize (less than 1 sec ago) - (q=_slots.estimate_get()) >= (sz<<1) ) // 1/2 of keys are dead? - newsz = oldlen<<1; // Double the existing size - - // Do not shrink, ever - if( newsz < oldlen ) newsz = oldlen; - - // Convert to power-of-2 - int log2; - for( log2=MIN_SIZE_LOG; (1<>20/*megs*/; - if( r >= 2 && megs > 0 ) { // Already 2 guys trying; wait and see - newkvs = _newkvs; // Between dorking around, another thread did it - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - // TODO - use a wait with timeout, so we'll wakeup as soon as the new table - // is ready, or after the timeout in any case. - //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup - // For now, sleep a tad and see if the 2 guys already trying to make - // the table actually get around to making it happen. - try { Thread.sleep(8*megs); } catch( Exception e ) { } - } - // Last check, since the 'new' below is expensive and there is a chance - // that another thread slipped in a new thread while we ran the heuristic. - newkvs = _newkvs; - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // Double size for K,V pairs, add 1 for CHM - newkvs = new Object[((1< _copyIdxUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyIdx"); - - // Work-done reporting. Used to efficiently signal when we can move to - // the new table. From 0 to len(oldkvs) refers to copying from the old - // table to the new. - volatile long _copyDone= 0; - static private final AtomicLongFieldUpdater _copyDoneUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyDone"); - - // --- help_copy_impl ---------------------------------------------------- - // Help along an existing resize operation. We hope its the top-level - // copy (it was when we started) but this CHM might have been promoted out - // of the top position. - private final void help_copy_impl( NonBlockingHashMap topmap, Object[] oldkvs, boolean copy_all ) { - assert chm(oldkvs) == this; - Object[] newkvs = _newkvs; - assert newkvs != null; // Already checked by caller - int oldlen = len(oldkvs); // Total amount to copy - final int MIN_COPY_WORK = Math.min(oldlen,1024); // Limit per-thread work - - // --- - int panic_start = -1; - int copyidx=-9999; // Fool javac to think it's initialized - while( _copyDone < oldlen ) { // Still needing to copy? - // Carve out a chunk of work. The counter wraps around so every - // thread eventually tries to copy every slot repeatedly. - - // We "panic" if we have tried TWICE to copy every slot - and it still - // has not happened. i.e., twice some thread somewhere claimed they - // would copy 'slot X' (by bumping _copyIdx) but they never claimed to - // have finished (by bumping _copyDone). Our choices become limited: - // we can wait for the work-claimers to finish (and become a blocking - // algorithm) or do the copy work ourselves. Tiny tables with huge - // thread counts trying to copy the table often 'panic'. - if( panic_start == -1 ) { // No panic? - copyidx = (int)_copyIdx; - while( copyidx < (oldlen<<1) && // 'panic' check - !_copyIdxUpdater.compareAndSet(this,copyidx,copyidx+MIN_COPY_WORK) ) - copyidx = (int)_copyIdx; // Re-read - if( !(copyidx < (oldlen<<1)) ) // Panic! - panic_start = copyidx; // Record where we started to panic-copy - } - - // We now know what to copy. Try to copy. - int workdone = 0; - for( int i=0; i 0 ) // Report work-done occasionally - copy_check_and_promote( topmap, oldkvs, workdone );// See if we can promote - //for( int i=0; i 0 ) { - while( !_copyDoneUpdater.compareAndSet(this,copyDone,copyDone+workdone) ) { - copyDone = _copyDone; // Reset, retry - assert (copyDone+workdone) <= oldlen; - } - //if( (10*copyDone/oldlen) != (10*(copyDone+workdone)/oldlen) ) - //System.out.print(" "+(copyDone+workdone)*100/oldlen+"%"+"_"+(_copyIdx*100/oldlen)+"%"); - } - - // Check for copy being ALL done, and promote. Note that we might have - // nested in-progress copies and manage to finish a nested copy before - // finishing the top-level copy. We only promote top-level copies. - if( copyDone+workdone == oldlen && // Ready to promote this table? - topmap._kvs == oldkvs && // Looking at the top-level table? - // Attempt to promote - topmap.CAS_kvs(oldkvs,_newkvs) ) { - topmap._last_resize_milli = System.currentTimeMillis(); // Record resize time for next check - //long nano = System.nanoTime(); - //System.out.println(" "+nano+" Promote table to "+len(_newkvs)); - //if( System.out != null ) System.out.print("]"); - } - } - - // --- copy_slot --------------------------------------------------------- - // Copy one K/V pair from oldkvs[i] to newkvs. Returns true if we can - // confirm that the new table guaranteed has a value for this old-table - // slot. We need an accurate confirmed-copy count so that we know when we - // can promote (if we promote the new table too soon, other threads may - // 'miss' on values not-yet-copied from the old table). We don't allow - // any direct updates on the new table, unless they first happened to the - // old table - so that any transition in the new table from null to - // not-null must have been from a copy_slot (or other old-table overwrite) - // and not from a thread directly writing in the new table. Thus we can - // count null-to-not-null transitions in the new table. - private boolean copy_slot( NonBlockingHashMap topmap, int idx, Object[] oldkvs, Object[] newkvs ) { - // Blindly set the key slot from null to TOMBSTONE, to eagerly stop - // fresh put's from inserting new values in the old table when the old - // table is mid-resize. We don't need to act on the results here, - // because our correctness stems from box'ing the Value field. Slamming - // the Key field is a minor speed optimization. - Object key; - while( (key=key(oldkvs,idx)) == null ) - CAS_key(oldkvs,idx, null, TOMBSTONE); - - // --- - // Prevent new values from appearing in the old table. - // Box what we see in the old table, to prevent further updates. - Object oldval = val(oldkvs,idx); // Read OLD table - while( !(oldval instanceof Prime) ) { - final Prime box = (oldval == null || oldval == TOMBSTONE) ? TOMBPRIME : new Prime(oldval); - if( CAS_val(oldkvs,idx,oldval,box) ) { // CAS down a box'd version of oldval - // If we made the Value slot hold a TOMBPRIME, then we both - // prevented further updates here but also the (absent) - // oldval is vaccuously available in the new table. We - // return with true here: any thread looking for a value for - // this key can correctly go straight to the new table and - // skip looking in the old table. - if( box == TOMBPRIME ) - return true; - // Otherwise we boxed something, but it still needs to be - // copied into the new table. - oldval = box; // Record updated oldval - break; // Break loop; oldval is now boxed by us - } - oldval = val(oldkvs,idx); // Else try, try again - } - if( oldval == TOMBPRIME ) return false; // Copy already complete here! - - // --- - // Copy the value into the new table, but only if we overwrite a null. - // If another value is already in the new table, then somebody else - // wrote something there and that write is happens-after any value that - // appears in the old table. If putIfMatch does not find a null in the - // new table - somebody else should have recorded the null-not_null - // transition in this copy. - Object old_unboxed = ((Prime)oldval)._V; - assert old_unboxed != TOMBSTONE; - boolean copied_into_new = (putIfMatch(topmap, newkvs, key, old_unboxed, null) == null); - - // --- - // Finally, now that any old value is exposed in the new table, we can - // forever hide the old-table value by slapping a TOMBPRIME down. This - // will stop other threads from uselessly attempting to copy this slot - // (i.e., it's a speed optimization not a correctness issue). - while( !CAS_val(oldkvs,idx,oldval,TOMBPRIME) ) - oldval = val(oldkvs,idx); - - return copied_into_new; - } // end copy_slot - } // End of CHM - - - // --- Snapshot ------------------------------------------------------------ - // The main class for iterating over the NBHM. It "snapshots" a clean - // view of the K/V array. - private class SnapshotV implements Iterator, Enumeration { - final Object[] _sskvs; - public SnapshotV() { - while( true ) { // Verify no table-copy-in-progress - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) { // No table-copy-in-progress - // The "linearization point" for the iteration. Every key in this - // table will be visited, but keys added later might be skipped or - // even be added to a following table (also not iterated over). - _sskvs = topkvs; - break; - } - // Table copy in-progress - so we cannot get a clean iteration. We - // must help finish the table copy before we can start iterating. - topchm.help_copy_impl(NonBlockingHashMap.this,topkvs,true); - } - // Warm-up the iterator - next(); - } - int length() { return len(_sskvs); } - Object key(int idx) { return NonBlockingHashMap.key(_sskvs,idx); } - private int _idx; // Varies from 0-keys.length - private Object _nextK, _prevK; // Last 2 keys found - private TypeV _nextV, _prevV; // Last 2 values found - public boolean hasNext() { return _nextV != null; } - public TypeV next() { - // 'next' actually knows what the next value will be - it had to - // figure that out last go-around lest 'hasNext' report true and - // some other thread deleted the last value. Instead, 'next' - // spends all its effort finding the key that comes after the - // 'next' key. - if( _idx != 0 && _nextV == null ) throw new NoSuchElementException(); - _prevK = _nextK; // This will become the previous key - _prevV = _nextV; // This will become the previous value - _nextV = null; // We have no more next-key - // Attempt to set <_nextK,_nextV> to the next K,V pair. - // _nextV is the trigger: stop searching when it is != null - while( _idx elements() { return new SnapshotV(); } - - // --- values -------------------------------------------------------------- - /** Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are reflected - * in the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - @Override - public Collection values() { - return new AbstractCollection() { - @Override public void clear ( ) { NonBlockingHashMap.this.clear ( ); } - @Override public int size ( ) { return NonBlockingHashMap.this.size ( ); } - @Override public boolean contains( Object v ) { return NonBlockingHashMap.this.containsValue(v); } - @Override public Iterator iterator() { return new SnapshotV(); } - }; - } - - // --- keySet -------------------------------------------------------------- - private class SnapshotK implements Iterator, Enumeration { - final SnapshotV _ss; - public SnapshotK() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public TypeK next() { _ss.next(); return (TypeK)_ss._prevK; } - public boolean hasNext() { return _ss.hasNext(); } - public TypeK nextElement() { return next(); } - public boolean hasMoreElements() { return hasNext(); } - } - - /** Returns an enumeration of the keys in this table. - * @return an enumeration of the keys in this table - * @see #keySet() */ - public Enumeration keys() { return new SnapshotK(); } - - /** Returns a {@link Set} view of the keys contained in this map. The set - * is backed by the map, so changes to the map are reflected in the set, - * and vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - @Override - public Set keySet() { - return new AbstractSet () { - @Override public void clear ( ) { NonBlockingHashMap.this.clear ( ); } - @Override public int size ( ) { return NonBlockingHashMap.this.size ( ); } - @Override public boolean contains( Object k ) { return NonBlockingHashMap.this.containsKey(k); } - @Override public boolean remove ( Object k ) { return NonBlockingHashMap.this.remove (k) != null; } - @Override public Iterator iterator() { return new SnapshotK(); } - }; - } - - - // --- entrySet ------------------------------------------------------------ - // Warning: Each call to 'next' in this iterator constructs a new NBHMEntry. - private class NBHMEntry extends AbstractEntry { - NBHMEntry( final TypeK k, final TypeV v ) { super(k,v); } - public TypeV setValue(final TypeV val) { - if( val == null ) throw new NullPointerException(); - _val = val; - return put(_key, val); - } - } - - private class SnapshotE implements Iterator> { - final SnapshotV _ss; - public SnapshotE() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public Entry next() { _ss.next(); return new NBHMEntry((TypeK)_ss._prevK,_ss._prevV); } - public boolean hasNext() { return _ss.hasNext(); } - } - - /** Returns a {@link Set} view of the mappings contained in this map. The - * set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes - * the corresponding mapping from the map, via the - * Iterator.remove, Set.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - * - *

Warning: the iterator associated with this Set - * requires the creation of {@link Entry} objects with each - * iteration. The {@link NonBlockingHashMap} does not normally create or - * using {@link Entry} objects so they will be created soley - * to support this iteration. Iterating using {@link #keySet} or {@link - * #values} will be more efficient. - */ - @Override - public Set> entrySet() { - return new AbstractSet>() { - @Override public void clear ( ) { NonBlockingHashMap.this.clear( ); } - @Override public int size ( ) { return NonBlockingHashMap.this.size ( ); } - @Override public boolean remove( final Object o ) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - return NonBlockingHashMap.this.remove(e.getKey(), e.getValue()); - } - @Override public boolean contains(final Object o) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - TypeV v = get(e.getKey()); - return v.equals(e.getValue()); - } - @Override public Iterator> iterator() { return new SnapshotE(); } - }; - } - - // --- writeObject ------------------------------------------------------- - // Write a NBHM to a stream - private void writeObject(java.io.ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); // Nothing to write - for( Object K : keySet() ) { - final Object V = get(K); // Do an official 'get' - s.writeObject(K); // Write the pair - s.writeObject(V); - } - s.writeObject(null); // Sentinel to indicate end-of-data - s.writeObject(null); - } - - // --- readObject -------------------------------------------------------- - // Read a CHM from a stream - private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { - s.defaultReadObject(); // Read nothing - initialize(MIN_SIZE); - for(;;) { - final TypeK K = (TypeK) s.readObject(); - final TypeV V = (TypeV) s.readObject(); - if( K == null ) break; - put(K,V); // Insert with an offical put - } - } - -} // End NonBlockingHashMap class diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMapLong.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMapLong.java deleted file mode 100644 index dce7919af..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashMapLong.java +++ /dev/null @@ -1,1220 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.IOException; -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; - -import org.cliffc.high_scale_lib.NonBlockingHashMap; -import sun.misc.Unsafe; -import java.lang.reflect.*; - -/** - * A lock-free alternate implementation of {@link java.util.ConcurrentHashMap} - * with primitive long keys, better scaling properties and - * generally lower costs. The use of {@code long} keys allows for faster - * compares and lower memory costs. The Map provides identical correctness - * properties as ConcurrentHashMap. All operations are non-blocking and - * multi-thread safe, including all update operations. {@link - * NonBlockingHashMapLong} scales substatially better than {@link - * java.util.ConcurrentHashMap} for high update rates, even with a large - * concurrency factor. Scaling is linear up to 768 CPUs on a 768-CPU Azul - * box, even with 100% updates or 100% reads or any fraction in-between. - * Linear scaling up to all cpus has been observed on a 32-way Sun US2 box, - * 32-way Sun Niagra box, 8-way Intel box and a 4-way Power box. - * - *

The main benefit of this class over using plain {@link - * org.cliffc.high_scale_lib.NonBlockingHashMap} with {@link Long} keys is - * that it avoids the auto-boxing and unboxing costs. Since auto-boxing is - * automatic, it is easy to accidentally cause auto-boxing and negate - * the space and speed benefits. - * - *

This class obeys the same functional specification as {@link - * Hashtable}, and includes versions of methods corresponding to - * each method of Hashtable. However, even though all operations are - * thread-safe, operations do not entail locking and there is - * not any support for locking the entire table in a way that - * prevents all access. This class is fully interoperable with - * Hashtable in programs that rely on its thread safety but not on - * its synchronization details. - * - *

Operations (including put) generally do not block, so may - * overlap with other update operations (including other puts and - * removes). Retrievals reflect the results of the most recently - * completed update operations holding upon their onset. For - * aggregate operations such as putAll, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, Iterators - * and Enumerations return elements reflecting the state of the hash table at - * some point at or since the creation of the iterator/enumeration. They do - * not throw {@link ConcurrentModificationException}. However, - * iterators are designed to be used by only one thread at a time. - * - *

Very full tables, or tables with high reprobe rates may trigger an - * internal resize operation to move into a larger table. Resizing is not - * terribly expensive, but it is not free either; during resize operations - * table throughput may drop somewhat. All threads that visit the table - * during a resize will 'help' the resizing but will still be allowed to - * complete their operation before the resize is finished (i.e., a simple - * 'get' operation on a million-entry table undergoing resizing will not need - * to block until the entire million entries are copied). - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow null to be used as a value. - * - * - * @since 1.5 - * @author Cliff Click - * @param the type of mapped values - */ - -public class NonBlockingHashMapLong - extends AbstractMap - implements ConcurrentMap, Serializable { - - private static final long serialVersionUID = 1234123412341234124L; - - private static final int REPROBE_LIMIT=10; // Too many reprobes then force a table-resize - - // --- Bits to allow Unsafe access to arrays - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - private static final int _Obase = _unsafe.arrayBaseOffset(Object[].class); - private static final int _Oscale = _unsafe.arrayIndexScale(Object[].class); - private static long rawIndex(final Object[] ary, final int idx) { - assert idx >= 0 && idx < ary.length; - return _Obase + idx * _Oscale; - } - private static final int _Lbase = _unsafe.arrayBaseOffset(long[].class); - private static final int _Lscale = _unsafe.arrayIndexScale(long[].class); - private static long rawIndex(final long[] ary, final int idx) { - assert idx >= 0 && idx < ary.length; - return _Lbase + idx * _Lscale; - } - - // --- Bits to allow Unsafe CAS'ing of the CHM field - private static final long _chm_offset; - private static final long _val_1_offset; - static { // - Field f = null; - try { f = NonBlockingHashMapLong.class.getDeclaredField("_chm"); } - catch( NoSuchFieldException e ) { throw new RuntimeException(e); } - _chm_offset = _unsafe.objectFieldOffset(f); - - try { f = NonBlockingHashMapLong.class.getDeclaredField("_val_1"); } - catch( NoSuchFieldException e ) { throw new RuntimeException(e); } - _val_1_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS( final long offset, final Object old, final Object nnn ) { - return _unsafe.compareAndSwapObject(this, offset, old, nnn ); - } - - // --- Adding a 'prime' bit onto Values via wrapping with a junk wrapper class - private static final class Prime { - final Object _V; - Prime( Object V ) { _V = V; } - static Object unbox( Object V ) { return V instanceof Prime ? ((Prime)V)._V : V; } - } - - // --- The Hash Table -------------------- - private transient CHM _chm; - // This next field holds the value for Key 0 - the special key value which - // is the initial array value, and also means: no-key-inserted-yet. - private transient Object _val_1; // Value for Key: NO_KEY - - // Time since last resize - private transient long _last_resize_milli; - - // Optimize for space: use a 1/2-sized table and allow more re-probes - private final boolean _opt_for_space; - - // --- Minimum table size ---------------- - // Pick size 16 K/V pairs, which turns into (16*2)*4+12 = 140 bytes on a - // standard 32-bit HotSpot, and (16*2)*8+12 = 268 bytes on 64-bit Azul. - private static final int MIN_SIZE_LOG=4; // - private static final int MIN_SIZE=(1<>2); - } - - // --- NonBlockingHashMapLong ---------------------------------------------- - // Constructors - /** Create a new NonBlockingHashMapLong with default minimum size (currently set - * to 8 K/V pairs or roughly 84 bytes on a standard 32-bit JVM). */ - public NonBlockingHashMapLong( ) { this(MIN_SIZE,true); } - - /** Create a new NonBlockingHashMapLong with initial room for the given - * number of elements, thus avoiding internal resizing operations to reach - * an appropriate size. Large numbers here when used with a small count of - * elements will sacrifice space for a small amount of time gained. The - * initial size will be rounded up internally to the next larger power of 2. */ - public NonBlockingHashMapLong( final int initial_sz ) { this(initial_sz,true); } - - /** Create a new NonBlockingHashMapLong, setting the space-for-speed - * tradeoff. {@code true} optimizes for space and is the default. {@code - * false} optimizes for speed and doubles space costs for roughly a 10% - * speed improvement. */ - public NonBlockingHashMapLong( final boolean opt_for_space ) { this(1,opt_for_space); } - - /** Create a new NonBlockingHashMapLong, setting both the initial size and - * the space-for-speed tradeoff. {@code true} optimizes for space and is - * the default. {@code false} optimizes for speed and doubles space costs - * for roughly a 10% speed improvement. */ - public NonBlockingHashMapLong( final int initial_sz, final boolean opt_for_space ) { - _opt_for_space = opt_for_space; - initialize(initial_sz); - } - private final void initialize( final int initial_sz ) { - if( initial_sz < 0 ) throw new IllegalArgumentException(); - int i; // Convert to next largest power-of-2 - for( i=MIN_SIZE_LOG; (1<true if the key is in the table */ - public boolean containsKey( long key ) { return get(key) != null; } - - /** Legacy method testing if some key maps into the specified value in this - * table. This method is identical in functionality to {@link - * #containsValue}, and exists solely to ensure full compatibility with - * class {@link Hashtable}, which supported this method prior to - * introduction of the Java Collections framework. - * @param val a value to search for - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean contains ( Object val ) { return containsValue(val); } - - /** Maps the specified key to the specified value in the table. The value - * cannot be null.

The value can be retrieved by calling {@link #get} - * with a key that is equal to the original key. - * @param key key with which the specified value is to be associated - * @param val value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified value is null */ - public TypeV put ( long key, TypeV val ) { return putIfMatch( key, val,NO_MATCH_OLD);} - - /** Atomically, do a {@link #put} if-and-only-if the key is not mapped. - * Useful to ensure that only a single mapping for the key exists, even if - * many threads are trying to create the mapping in parallel. - * @return the previous value associated with the specified key, - * or null if there was no mapping for the key - * @throws NullPointerException if the specified is value is null */ - public TypeV putIfAbsent( long key, TypeV val ) { return putIfMatch( key, val,TOMBSTONE );} - - /** Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * @return the previous value associated with key, or - * null if there was no mapping for key*/ - public TypeV remove ( long key ) { return putIfMatch( key,TOMBSTONE,NO_MATCH_OLD);} - - /** Atomically do a {@link #remove(long)} if-and-only-if the key is mapped - * to a value which is equals to the given value. - * @throws NullPointerException if the specified value is null */ - public boolean remove ( long key,Object val ) { return putIfMatch( key,TOMBSTONE,val ) == val ;} - - /** Atomically do a put(key,val) if-and-only-if the key is - * mapped to some value already. - * @throws NullPointerException if the specified value is null */ - public TypeV replace ( long key, TypeV val ) { return putIfMatch( key, val,MATCH_ANY );} - - /** Atomically do a put(key,newValue) if-and-only-if the key is - * mapped a value which is equals to oldValue. - * @throws NullPointerException if the specified value is null */ - public boolean replace ( long key, TypeV oldValue, TypeV newValue ) { - return putIfMatch( key, newValue, oldValue ) == oldValue; - } - - private final TypeV putIfMatch( long key, Object newVal, Object oldVal ) { - if (oldVal == null || newVal == null) throw new NullPointerException(); - if( key == NO_KEY ) { - final Object curVal = _val_1; - if( oldVal == NO_MATCH_OLD || // Do we care about expected-Value at all? - curVal == oldVal || // No instant match already? - (oldVal == MATCH_ANY && curVal != TOMBSTONE) || - oldVal.equals(curVal) ) // Expensive equals check - CAS(_val_1_offset,curVal,newVal); // One shot CAS update attempt - return curVal == TOMBSTONE ? null : (TypeV)curVal; // Return the last value present - } - final Object res = _chm.putIfMatch( key, newVal, oldVal ); - assert !(res instanceof Prime); - assert res != null; - return res == TOMBSTONE ? null : (TypeV)res; - } - - /** Removes all of the mappings from this map. */ - public void clear() { // Smack a new empty table down - CHM newchm = new CHM(this,new Counter(),MIN_SIZE_LOG); - while( !CAS(_chm_offset,_chm,newchm) ) // Spin until the clear works - ; - CAS(_val_1_offset,_val_1,TOMBSTONE); - } - - /** Returns true if this Map maps one or more keys to the specified - * value. Note: This method requires a full internal traversal of the - * hash table and is much slower than {@link #containsKey}. - * @param val value whose presence in this map is to be tested - * @return true if this Map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean containsValue( Object val ) { - if( val == null ) return false; - if( val == _val_1 ) return true; // Key 0 - for( TypeV V : values() ) - if( V == val || V.equals(val) ) - return true; - return false; - } - - // --- get ----------------------------------------------------------------- - /** Returns the value to which the specified key is mapped, or {@code null} - * if this map contains no mapping for the key. - *

More formally, if this map contains a mapping from a key {@code k} to - * a value {@code v} such that {@code key==k}, then this method - * returns {@code v}; otherwise it returns {@code null}. (There can be at - * most one such mapping.) - * @throws NullPointerException if the specified key is null */ - // Never returns a Prime nor a Tombstone. - public final TypeV get( long key ) { - if( key == NO_KEY ) { - final Object V = _val_1; - return V == TOMBSTONE ? null : (TypeV)V; - } - final Object V = _chm.get_impl(key); - assert !(V instanceof Prime); // Never return a Prime - assert V != TOMBSTONE; - return (TypeV)V; - } - - /** Auto-boxing version of {@link #get(long)}. */ - public TypeV get ( Object key ) { return (key instanceof Long) ? get (((Long)key).longValue()) : null; } - /** Auto-boxing version of {@link #remove(long)}. */ - public TypeV remove ( Object key ) { return (key instanceof Long) ? remove (((Long)key).longValue()) : null; } - /** Auto-boxing version of {@link #remove(long,Object)}. */ - public boolean remove ( Object key, Object Val ) { return (key instanceof Long) ? remove (((Long)key).longValue(), Val) : false; } - /** Auto-boxing version of {@link #containsKey(long)}. */ - public boolean containsKey( Object key ) { return (key instanceof Long) ? containsKey(((Long)key).longValue()) : false; } - /** Auto-boxing version of {@link #putIfAbsent}. */ - public TypeV putIfAbsent( Long key, TypeV val ) { return putIfAbsent( ((Long)key).longValue(), val ); } - /** Auto-boxing version of {@link #replace}. */ - public TypeV replace( Long key, TypeV Val ) { return replace(((Long)key).longValue(), Val); } - /** Auto-boxing version of {@link #put}. */ - public TypeV put ( Long key, TypeV val ) { return put(key.longValue(),val); } - /** Auto-boxing version of {@link #replace}. */ - public boolean replace( Long key, TypeV oldValue, TypeV newValue ) { - return replace(((Long)key).longValue(), oldValue, newValue); - } - - // --- help_copy ----------------------------------------------------------- - // Help along an existing resize operation. This is just a fast cut-out - // wrapper, to encourage inlining for the fast no-copy-in-progress case. We - // always help the top-most table copy, even if there are nested table - // copies in progress. - private final void help_copy( ) { - // Read the top-level CHM only once. We'll try to help this copy along, - // even if it gets promoted out from under us (i.e., the copy completes - // and another KVS becomes the top-level copy). - CHM topchm = _chm; - if( topchm._newchm == null ) return; // No copy in-progress - topchm.help_copy_impl(false); - } - - - // --- CHM ----------------------------------------------------------------- - // The control structure for the NonBlockingHashMapLong - private static final class CHM implements Serializable { - // Back-pointer to top-level structure - final NonBlockingHashMapLong _nbhml; - - // Size in active K,V pairs - private final Counter _size; - public int size () { return (int)_size.get(); } - - // --- - // These next 2 fields are used in the resizing heuristics, to judge when - // it is time to resize or copy the table. Slots is a count of used-up - // key slots, and when it nears a large fraction of the table we probably - // end up reprobing too much. Last-resize-milli is the time since the - // last resize; if we are running back-to-back resizes without growing - // (because there are only a few live keys but many slots full of dead - // keys) then we need a larger table to cut down on the churn. - - // Count of used slots, to tell when table is full of dead unusable slots - private final Counter _slots; - public int slots() { return (int)_slots.get(); } - - // --- - // New mappings, used during resizing. - // The 'next' CHM - created during a resize operation. This represents - // the new table being copied from the old one. It's the volatile - // variable that is read as we cross from one table to the next, to get - // the required memory orderings. It monotonically transits from null to - // set (once). - volatile CHM _newchm; - private static final AtomicReferenceFieldUpdater _newchmUpdater = - AtomicReferenceFieldUpdater.newUpdater(CHM.class,CHM.class, "_newchm"); - // Set the _newchm field if we can. AtomicUpdaters do not fail spuriously. - boolean CAS_newchm( CHM newchm ) { - return _newchmUpdater.compareAndSet(this,null,newchm); - } - // Sometimes many threads race to create a new very large table. Only 1 - // wins the race, but the losers all allocate a junk large table with - // hefty allocation costs. Attempt to control the overkill here by - // throttling attempts to create a new table. I cannot really block here - // (lest I lose the non-blocking property) but late-arriving threads can - // give the initial resizing thread a little time to allocate the initial - // new table. The Right Long Term Fix here is to use array-lets and - // incrementally create the new very large array. In C I'd make the array - // with malloc (which would mmap under the hood) which would only eat - // virtual-address and not real memory - and after Somebody wins then we - // could in parallel initialize the array. Java does not allow - // un-initialized array creation (especially of ref arrays!). - volatile long _resizers; // count of threads attempting an initial resize - private static final AtomicLongFieldUpdater _resizerUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_resizers"); - - // --- key,val ------------------------------------------------------------- - // Access K,V for a given idx - private final boolean CAS_key( int idx, long old, long key ) { - return _unsafe.compareAndSwapLong ( _keys, rawIndex(_keys, idx), old, key ); - } - private final boolean CAS_val( int idx, Object old, Object val ) { - return _unsafe.compareAndSwapObject( _vals, rawIndex(_vals, idx), old, val ); - } - - final long [] _keys; - final Object [] _vals; - - // Simple constructor - CHM( final NonBlockingHashMapLong nbhml, Counter size, final int logsize ) { - _nbhml = nbhml; - _size = size; - _slots= new Counter(); - _keys = new long [1<= reprobe_limit(len) ) // too many probes - return _newchm == null // Table copy in progress? - ? null // Nope! A clear miss - : copy_slot_and_check(idx,key).get_impl(key); // Retry in the new table - - idx = (idx+1)&(len-1); // Reprobe by 1! (could now prefetch) - } - } - - // --- putIfMatch --------------------------------------------------------- - // Put, Remove, PutIfAbsent, etc. Return the old value. If the returned - // value is equal to expVal (or expVal is NO_MATCH_OLD) then the put can - // be assumed to work (although might have been immediately overwritten). - // Only the path through copy_slot passes in an expected value of null, - // and putIfMatch only returns a null if passed in an expected null. - private final Object putIfMatch( final long key, final Object putval, final Object expVal ) { - assert putval != null; - assert !(putval instanceof Prime); - assert !(expVal instanceof Prime); - final int len = _keys.length; - int idx = (int)(key & (len-1)); // The first key - - // --- - // Key-Claim stanza: spin till we can claim a Key (or force a resizing). - int reprobe_cnt=0; - long K = NO_KEY; - Object V = null; - while( true ) { // Spin till we get a Key slot - V = _vals[idx]; // Get old value - K = _keys[idx]; // Get current key - if( K == NO_KEY ) { // Slot is free? - // Found an empty Key slot - which means this Key has never been in - // this table. No need to put a Tombstone - the Key is not here! - if( putval == TOMBSTONE ) return putval; // Not-now & never-been in this table - // Claim the zero key-slot - if( CAS_key(idx, NO_KEY, key) ) { // Claim slot for Key - _slots.add(1); // Raise key-slots-used count - break; // Got it! - } - // CAS to claim the key-slot failed. - // - // This re-read of the Key points out an annoying short-coming of Java - // CAS. Most hardware CAS's report back the existing value - so that - // if you fail you have a *witness* - the value which caused the CAS - // to fail. The Java API turns this into a boolean destroying the - // witness. Re-reading does not recover the witness because another - // thread can write over the memory after the CAS. Hence we can be in - // the unfortunate situation of having a CAS fail *for cause* but - // having that cause removed by a later store. This turns a - // non-spurious-failure CAS (such as Azul has) into one that can - // apparently spuriously fail - and we avoid apparent spurious failure - // by not allowing Keys to ever change. - K = _keys[idx]; // CAS failed, get updated value - assert K != NO_KEY ; // If keys[idx] is NO_KEY, CAS shoulda worked - } - // Key slot was not null, there exists a Key here - if( K == key ) - break; // Got it! - - // get and put must have the same key lookup logic! Lest 'get' give - // up looking too soon. - //topmap._reprobes.add(1); - if( ++reprobe_cnt >= reprobe_limit(len) ) { - // We simply must have a new table to do a 'put'. At this point a - // 'get' will also go to the new table (if any). We do not need - // to claim a key slot (indeed, we cannot find a free one to claim!). - final CHM newchm = resize(); - if( expVal != null ) _nbhml.help_copy(); // help along an existing copy - return newchm.putIfMatch(key,putval,expVal); - } - - idx = (idx+1)&(len-1); // Reprobe! - } // End of spinning till we get a Key slot - - // --- - // Found the proper Key slot, now update the matching Value slot. We - // never put a null, so Value slots monotonically move from null to - // not-null (deleted Values use Tombstone). Thus if 'V' is null we - // fail this fast cutout and fall into the check for table-full. - if( putval == V ) return V; // Fast cutout for no-change - - // See if we want to move to a new table (to avoid high average re-probe - // counts). We only check on the initial set of a Value from null to - // not-null (i.e., once per key-insert). - if( (V == null && tableFull(reprobe_cnt,len)) || - // Or we found a Prime: resize is already in progress. The resize - // call below will do a CAS on _newchm forcing the read. - V instanceof Prime) { - resize(); // Force the new table copy to start - return copy_slot_and_check(idx,expVal).putIfMatch(key,putval,expVal); - } - - // --- - // We are finally prepared to update the existing table - while( true ) { - assert !(V instanceof Prime); - - // Must match old, and we do not? Then bail out now. Note that either V - // or expVal might be TOMBSTONE. Also V can be null, if we've never - // inserted a value before. expVal can be null if we are called from - // copy_slot. - - if( expVal != NO_MATCH_OLD && // Do we care about expected-Value at all? - V != expVal && // No instant match already? - (expVal != MATCH_ANY || V == TOMBSTONE || V == null) && - !(V==null && expVal == TOMBSTONE) && // Match on null/TOMBSTONE combo - (expVal == null || !expVal.equals(V)) ) // Expensive equals check at the last - return V; // Do not update! - - // Actually change the Value in the Key,Value pair - if( CAS_val(idx, V, putval ) ) { - // CAS succeeded - we did the update! - // Both normal put's and table-copy calls putIfMatch, but table-copy - // does not (effectively) increase the number of live k/v pairs. - if( expVal != null ) { - // Adjust sizes - a striped counter - if( (V == null || V == TOMBSTONE) && putval != TOMBSTONE ) _size.add( 1); - if( !(V == null || V == TOMBSTONE) && putval == TOMBSTONE ) _size.add(-1); - } - return (V==null && expVal!=null) ? TOMBSTONE : V; - } - // Else CAS failed - V = _vals[idx]; // Get new value - // If a Prime'd value got installed, we need to re-run the put on the - // new table. Otherwise we lost the CAS to another racing put. - // Simply retry from the start. - if( V instanceof Prime ) - return copy_slot_and_check(idx,expVal).putIfMatch(key,putval,expVal); - } - } - - // --- tableFull --------------------------------------------------------- - // Heuristic to decide if this table is too full, and we should start a - // new table. Note that if a 'get' call has reprobed too many times and - // decided the table must be full, then always the estimate_sum must be - // high and we must report the table is full. If we do not, then we might - // end up deciding that the table is not full and inserting into the - // current table, while a 'get' has decided the same key cannot be in this - // table because of too many reprobes. The invariant is: - // slots.estimate_sum >= max_reprobe_cnt >= reprobe_limit(len) - private final boolean tableFull( int reprobe_cnt, int len ) { - return - // Do the cheap check first: we allow some number of reprobes always - reprobe_cnt >= REPROBE_LIMIT && - // More expensive check: see if the table is > 1/4 full. - _slots.estimate_get() >= reprobe_limit(len); - } - - // --- resize ------------------------------------------------------------ - // Resizing after too many probes. "How Big???" heuristics are here. - // Callers will (not this routine) will 'help_copy' any in-progress copy. - // Since this routine has a fast cutout for copy-already-started, callers - // MUST 'help_copy' lest we have a path which forever runs through - // 'resize' only to discover a copy-in-progress which never progresses. - private final CHM resize() { - // Check for resize already in progress, probably triggered by another thread - CHM newchm = _newchm; // VOLATILE READ - if( newchm != null ) // See if resize is already in progress - return newchm; // Use the new table already - - // No copy in-progress, so start one. First up: compute new table size. - int oldlen = _keys.length; // Old count of K,V pairs allowed - int sz = size(); // Get current table count of active K,V pairs - int newsz = sz; // First size estimate - - // Heuristic to determine new size. We expect plenty of dead-slots-with-keys - // and we need some decent padding to avoid endless reprobing. - if( _nbhml._opt_for_space ) { - // This heuristic leads to a much denser table with a higher reprobe rate - if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - newsz = oldlen<<1; // Double size - } else { - if( sz >= (oldlen>>2) ) { // If we are >25% full of keys then... - newsz = oldlen<<1; // Double size - if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - newsz = oldlen<<2; // Double double size - } - } - - // Last (re)size operation was very recent? Then double again; slows - // down resize operations for tables subject to a high key churn rate. - long tm = System.currentTimeMillis(); - long q=0; - if( newsz <= oldlen && // New table would shrink or hold steady? - tm <= _nbhml._last_resize_milli+10000 && // Recent resize (less than 1 sec ago) - //(q=_slots.estimate_sum()) >= (sz<<1) ) // 1/2 of keys are dead? - true ) - newsz = oldlen<<1; // Double the existing size - - // Do not shrink, ever - if( newsz < oldlen ) newsz = oldlen; - //System.out.println("old="+oldlen+" new="+newsz+" size()="+sz+" est_slots()="+q+" millis="+(tm-_nbhml._last_resize_milli)); - - // Convert to power-of-2 - int log2; - for( log2=MIN_SIZE_LOG; (1<>20/*megs*/; - if( r >= 2 && megs > 0 ) { // Already 2 guys trying; wait and see - newchm = _newchm; // Between dorking around, another thread did it - if( newchm != null ) // See if resize is already in progress - return newchm; // Use the new table already - // TODO - use a wait with timeout, so we'll wakeup as soon as the new table - // is ready, or after the timeout in any case. - //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup - // For now, sleep a tad and see if the 2 guys already trying to make - // the table actually get around to making it happen. - try { Thread.sleep(8*megs); } catch( Exception e ) { } - } - // Last check, since the 'new' below is expensive and there is a chance - // that another thread slipped in a new thread while we ran the heuristic. - newchm = _newchm; - if( newchm != null ) // See if resize is already in progress - return newchm; // Use the new table already - - // New CHM - actually allocate the big arrays - newchm = new CHM(_nbhml,_size,log2); - - // Another check after the slow allocation - if( _newchm != null ) // See if resize is already in progress - return _newchm; // Use the new table already - - // The new table must be CAS'd in so only 1 winner amongst duplicate - // racing resizing threads. Extra CHM's will be GC'd. - if( CAS_newchm( newchm ) ) { // NOW a resize-is-in-progress! - //notifyAll(); // Wake up any sleepers - //long nano = System.nanoTime(); - //System.out.println(" "+nano+" Resize from "+oldlen+" to "+(1< _copyIdxUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyIdx"); - - // Work-done reporting. Used to efficiently signal when we can move to - // the new table. From 0 to len(oldkvs) refers to copying from the old - // table to the new. - volatile long _copyDone= 0; - static private final AtomicLongFieldUpdater _copyDoneUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyDone"); - - // --- help_copy_impl ---------------------------------------------------- - // Help along an existing resize operation. We hope its the top-level - // copy (it was when we started) but this CHM might have been promoted out - // of the top position. - private final void help_copy_impl( final boolean copy_all ) { - final CHM newchm = _newchm; - assert newchm != null; // Already checked by caller - int oldlen = _keys.length; // Total amount to copy - final int MIN_COPY_WORK = Math.min(oldlen,1024); // Limit per-thread work - - // --- - int panic_start = -1; - int copyidx=-9999; // Fool javac to think it's initialized - while( _copyDone < oldlen ) { // Still needing to copy? - // Carve out a chunk of work. The counter wraps around so every - // thread eventually tries to copy every slot repeatedly. - - // We "panic" if we have tried TWICE to copy every slot - and it still - // has not happened. i.e., twice some thread somewhere claimed they - // would copy 'slot X' (by bumping _copyIdx) but they never claimed to - // have finished (by bumping _copyDone). Our choices become limited: - // we can wait for the work-claimers to finish (and become a blocking - // algorithm) or do the copy work ourselves. Tiny tables with huge - // thread counts trying to copy the table often 'panic'. - if( panic_start == -1 ) { // No panic? - copyidx = (int)_copyIdx; - while( copyidx < (oldlen<<1) && // 'panic' check - !_copyIdxUpdater.compareAndSet(this,copyidx,copyidx+MIN_COPY_WORK) ) - copyidx = (int)_copyIdx; // Re-read - if( !(copyidx < (oldlen<<1)) ) // Panic! - panic_start = copyidx; // Record where we started to panic-copy - } - - // We now know what to copy. Try to copy. - int workdone = 0; - for( int i=0; i 0 ) // Report work-done occasionally - copy_check_and_promote( workdone );// See if we can promote - //for( int i=0; i 0 ) { - while( !_copyDoneUpdater.compareAndSet(this,copyDone,nowDone) ) { - copyDone = _copyDone; // Reset, retry - nowDone = copyDone+workdone; - assert nowDone <= oldlen; - } - //if( (10*copyDone/oldlen) != (10*nowDone/oldlen) ) - // System.out.print(" "+nowDone*100/oldlen+"%"+"_"+(_copyIdx*100/oldlen)+"%"); - } - - // Check for copy being ALL done, and promote. Note that we might have - // nested in-progress copies and manage to finish a nested copy before - // finishing the top-level copy. We only promote top-level copies. - if( nowDone == oldlen && // Ready to promote this table? - _nbhml._chm == this && // Looking at the top-level table? - // Attempt to promote - _nbhml.CAS(_chm_offset,this,_newchm) ) { - _nbhml._last_resize_milli = System.currentTimeMillis(); // Record resize time for next check - //long nano = System.nanoTime(); - //System.out.println(" "+nano+" Promote table "+oldlen+" to "+_newchm._keys.length); - //System.out.print("_"+oldlen+"]"); - } - } - - // --- copy_slot --------------------------------------------------------- - // Copy one K/V pair from oldkvs[i] to newkvs. Returns true if we can - // confirm that the new table guaranteed has a value for this old-table - // slot. We need an accurate confirmed-copy count so that we know when we - // can promote (if we promote the new table too soon, other threads may - // 'miss' on values not-yet-copied from the old table). We don't allow - // any direct updates on the new table, unless they first happened to the - // old table - so that any transition in the new table from null to - // not-null must have been from a copy_slot (or other old-table overwrite) - // and not from a thread directly writing in the new table. Thus we can - // count null-to-not-null transitions in the new table. - private boolean copy_slot( int idx ) { - // Blindly set the key slot from NO_KEY to some key which hashes here, - // to eagerly stop fresh put's from inserting new values in the old - // table when the old table is mid-resize. We don't need to act on the - // results here, because our correctness stems from box'ing the Value - // field. Slamming the Key field is a minor speed optimization. - long key; - while( (key=_keys[idx]) == NO_KEY ) - CAS_key(idx, NO_KEY, (idx+_keys.length)/*a non-zero key which hashes here*/); - - // --- - // Prevent new values from appearing in the old table. - // Box what we see in the old table, to prevent further updates. - Object oldval = _vals[idx]; // Read OLD table - while( !(oldval instanceof Prime) ) { - final Prime box = (oldval == null || oldval == TOMBSTONE) ? TOMBPRIME : new Prime(oldval); - if( CAS_val(idx,oldval,box) ) { // CAS down a box'd version of oldval - // If we made the Value slot hold a TOMBPRIME, then we both - // prevented further updates here but also the (absent) oldval is - // vaccuously available in the new table. We return with true here: - // any thread looking for a value for this key can correctly go - // straight to the new table and skip looking in the old table. - if( box == TOMBPRIME ) - return true; - // Otherwise we boxed something, but it still needs to be - // copied into the new table. - oldval = box; // Record updated oldval - break; // Break loop; oldval is now boxed by us - } - oldval = _vals[idx]; // Else try, try again - } - if( oldval == TOMBPRIME ) return false; // Copy already complete here! - - // --- - // Copy the value into the new table, but only if we overwrite a null. - // If another value is already in the new table, then somebody else - // wrote something there and that write is happens-after any value that - // appears in the old table. If putIfMatch does not find a null in the - // new table - somebody else should have recorded the null-not_null - // transition in this copy. - Object old_unboxed = ((Prime)oldval)._V; - assert old_unboxed != TOMBSTONE; - boolean copied_into_new = (_newchm.putIfMatch(key, old_unboxed, null) == null); - - // --- - // Finally, now that any old value is exposed in the new table, we can - // forever hide the old-table value by slapping a TOMBPRIME down. This - // will stop other threads from uselessly attempting to copy this slot - // (i.e., it's a speed optimization not a correctness issue). - while( !CAS_val(idx,oldval,TOMBPRIME) ) - oldval = _vals[idx]; - - return copied_into_new; - } // end copy_slot - } // End of CHM - - - // --- Snapshot ------------------------------------------------------------ - private class SnapshotV implements Iterator, Enumeration { - final CHM _sschm; - public SnapshotV() { - CHM topchm; - while( true ) { // Verify no table-copy-in-progress - topchm = _chm; - if( topchm._newchm == null ) // No table-copy-in-progress - break; - // Table copy in-progress - so we cannot get a clean iteration. We - // must help finish the table copy before we can start iterating. - topchm.help_copy_impl(true); - } - // The "linearization point" for the iteration. Every key in this table - // will be visited, but keys added later might be skipped or even be - // added to a following table (also not iterated over). - _sschm = topchm; - // Warm-up the iterator - _idx = -1; - next(); - } - int length() { return _sschm._keys.length; } - long key(final int idx) { return _sschm._keys[idx]; } - private int _idx; // -2 for NO_KEY, -1 for CHECK_NEW_TABLE_LONG, 0-keys.length - private long _nextK, _prevK; // Last 2 keys found - private TypeV _nextV, _prevV; // Last 2 values found - public boolean hasNext() { return _nextV != null; } - public TypeV next() { - // 'next' actually knows what the next value will be - it had to - // figure that out last go 'round lest 'hasNext' report true and - // some other thread deleted the last value. Instead, 'next' - // spends all its effort finding the key that comes after the - // 'next' key. - if( _idx != -1 && _nextV == null ) throw new NoSuchElementException(); - _prevK = _nextK; // This will become the previous key - _prevV = _nextV; // This will become the previous value - _nextV = null; // We have no more next-key - // Attempt to set <_nextK,_nextV> to the next K,V pair. - // _nextV is the trigger: stop searching when it is != null - if( _idx == -1 ) { // Check for NO_KEY - _idx = 0; // Setup for next phase of search - _nextK = NO_KEY; - if( (_nextV=get(_nextK)) != null ) return _prevV; - } - while( _idx elements() { return new SnapshotV(); } - - // --- values -------------------------------------------------------------- - /** Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are reflected - * in the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - public Collection values() { - return new AbstractCollection() { - public void clear ( ) { NonBlockingHashMapLong.this.clear ( ); } - public int size ( ) { return NonBlockingHashMapLong.this.size ( ); } - public boolean contains( Object v ) { return NonBlockingHashMapLong.this.containsValue(v); } - public Iterator iterator() { return new SnapshotV(); } - }; - } - - // --- keySet -------------------------------------------------------------- - /** A class which implements the {@link Iterator} and {@link Enumeration} - * interfaces, generified to the {@link Long} class and supporting a - * non-auto-boxing {@link #nextLong} function. */ - public class IteratorLong implements Iterator, Enumeration { - private final SnapshotV _ss; - /** A new IteratorLong */ - public IteratorLong() { _ss = new SnapshotV(); } - /** Remove last key returned by {@link #next} or {@link #nextLong}. */ - public void remove() { _ss.remove(); } - /** Auto-box and return the next key. */ - public Long next () { _ss.next(); return _ss._prevK; } - /** Return the next key as a primitive {@code long}. */ - public long nextLong() { _ss.next(); return _ss._prevK; } - /** True if there are more keys to iterate over. */ - public boolean hasNext() { return _ss.hasNext(); } - /** Auto-box and return the next key. */ - public Long nextElement() { return next(); } - /** True if there are more keys to iterate over. */ - public boolean hasMoreElements() { return hasNext(); } - } - /** Returns an enumeration of the auto-boxed keys in this table. - * Warning: this version will auto-box all returned keys. - * @return an enumeration of the auto-boxed keys in this table - * @see #keySet() */ - public Enumeration keys() { return new IteratorLong(); } - - /** Returns a {@link Set} view of the keys contained in this map; with care - * the keys may be iterated over without auto-boxing. The - * set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes - * the corresponding mapping from this map, via the - * Iterator.remove, Set.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - public Set keySet() { - return new AbstractSet () { - public void clear ( ) { NonBlockingHashMapLong.this.clear ( ); } - public int size ( ) { return NonBlockingHashMapLong.this.size ( ); } - public boolean contains( Object k ) { return NonBlockingHashMapLong.this.containsKey(k); } - public boolean remove ( Object k ) { return NonBlockingHashMapLong.this.remove (k) != null; } - public IteratorLong iterator() { return new IteratorLong(); } - }; - } - - - // --- entrySet ------------------------------------------------------------ - // Warning: Each call to 'next' in this iterator constructs a new Long and a - // new NBHMLEntry. - private class NBHMLEntry extends AbstractEntry { - NBHMLEntry( final Long k, final TypeV v ) { super(k,v); } - public TypeV setValue(final TypeV val) { - if (val == null) throw new NullPointerException(); - _val = val; - return put(_key, val); - } - } - private class SnapshotE implements Iterator> { - final SnapshotV _ss; - public SnapshotE() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public Entry next() { _ss.next(); return new NBHMLEntry(_ss._prevK,_ss._prevV); } - public boolean hasNext() { return _ss.hasNext(); } - } - - /** Returns a {@link Set} view of the mappings contained in this map. The - * set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes - * the corresponding mapping from the map, via the - * Iterator.remove, Set.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - * - *

Warning: the iterator associated with this Set - * requires the creation of {@link Entry} objects with each - * iteration. The {@link NonBlockingHashMap} - * does not normally create or using {@link Entry} objects so - * they will be created soley to support this iteration. Iterating using - * {@link #keySet} or {@link #values} will be more efficient. In addition, - * this version requires auto-boxing the keys. - */ - public Set> entrySet() { - return new AbstractSet>() { - public void clear ( ) { NonBlockingHashMapLong.this.clear( ); } - public int size ( ) { return NonBlockingHashMapLong.this.size ( ); } - public boolean remove( final Object o ) { - if (!(o instanceof Entry)) return false; - final Entry e = (Entry)o; - return NonBlockingHashMapLong.this.remove(e.getKey(), e.getValue()); - } - public boolean contains(final Object o) { - if (!(o instanceof Entry)) return false; - final Entry e = (Entry)o; - TypeV v = get(e.getKey()); - return v.equals(e.getValue()); - } - public Iterator> iterator() { return new SnapshotE(); } - }; - } - - // --- writeObject ------------------------------------------------------- - // Write a NBHML to a stream - private void writeObject(java.io.ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); // Write nothing - for( long K : keySet() ) { - final Object V = get(K); // Do an official 'get' - s.writeLong (K); // Write the pair - s.writeObject(V); - } - s.writeLong(NO_KEY); // Sentinel to indicate end-of-data - s.writeObject(null); - } - - // --- readObject -------------------------------------------------------- - // Read a CHM from a stream - private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { - s.defaultReadObject(); // Read nothing - initialize(MIN_SIZE); - for (;;) { - final long K = s.readLong(); - final TypeV V = (TypeV) s.readObject(); - if( K == NO_KEY && V == null ) break; - put(K,V); // Insert with an offical put - } - } - -} // End NonBlockingHashMapLong class diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashSet.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashSet.java deleted file mode 100644 index 55c30b5cd..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashSet.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.Serializable; -import java.util.*; - - -/** - * A simple wrapper around {@link NonBlockingHashMap} making it implement the - * {@link Set} interface. All operations are Non-Blocking and multi-thread safe. - * - * @since 1.5 - * @author Cliff Click - */ - - -public class NonBlockingHashSet extends AbstractSet implements Serializable { - private static final Object V = ""; - - private final NonBlockingHashMap _map; - - /** Make a new empty {@link NonBlockingHashSet}. */ - public NonBlockingHashSet() { super(); _map = new NonBlockingHashMap(); } - - /** Add {@code o} to the set. - * @return true if {@code o} was added to the set, false - * if {@code o} was already in the set. - */ - public boolean add ( final E o ) { return _map.putIfAbsent(o,V) != V; } - - /** - * @return true if {@code o} is in the set. - */ - public boolean contains ( final Object o ) { return _map.containsKey(o); } - /** Remove {@code o} from the set. - * @return true if {@code o} was removed to the set, false - * if {@code o} was not in the set. - */ - public boolean remove ( final Object o ) { return _map.remove(o) == V; } - /** - * Current count of elements in the set. Due to concurrent racing updates, - * the size is only ever approximate. Updates due to the calling thread are - * immediately visible to calling thread. - * @return count of elements. - */ - public int size ( ) { return _map.size(); } - /** Empty the set. */ - public void clear ( ) { _map.clear(); } - - public Iteratoriterator( ) { return _map.keySet().iterator(); } - - // --- - - /** - * Atomically make the set immutable. Future calls to mutate will throw an - * IllegalStateException. Existing mutator calls in other threads racing - * with this thread and will either throw IllegalStateException or their - * update will be visible to this thread. This implies that a simple flag - * cannot make the Set immutable, because a late-arriving update in another - * thread might see immutable flag not set yet, then mutate the Set after - * the {@link #readOnly} call returns. This call can be called concurrently - * (and indeed until the operation completes, all calls on the Set from any - * thread either complete normally or end up calling {@link #readOnly} - * internally). - * - *

This call is useful in debugging multi-threaded programs where the - * Set is constructed in parallel, but construction completes after some - * time; and after construction the Set is only read. Making the Set - * read-only will cause updates arriving after construction is supposedly - * complete to throw an {@link IllegalStateException}. - */ - - // (1) call _map's immutable() call - // (2) get snapshot - // (3) CAS down a local map, power-of-2 larger than _map.size()+1/8th - // (4) start @ random, visit all snapshot, insert live keys - // (5) CAS _map to null, needs happens-after (4) - // (6) if Set call sees _map is null, needs happens-after (4) for readers - public void readOnly() { - throw new RuntimeException("Unimplemented"); - } -} diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashtable.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashtable.java deleted file mode 100644 index d513b1657..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingHashtable.java +++ /dev/null @@ -1,1308 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -/* WARNING: MACHINE GENERATED FILE! DO NOT EDIT!*/ -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.*; - -import sun.misc.Unsafe; - -/** - * A lock-free alternate implementation of {@link java.util.concurrent.ConcurrentHashMap} - * with better scaling properties and generally lower costs to mutate the Map. - * It provides identical correctness properties as ConcurrentHashMap. All - * operations are non-blocking and multi-thread safe, including all update - * operations. {@link NonBlockingHashtable} scales substatially better than - * {@link java.util.concurrent.ConcurrentHashMap} for high update rates, even with a - * large concurrency factor. Scaling is linear up to 768 CPUs on a 768-CPU - * Azul box, even with 100% updates or 100% reads or any fraction in-between. - * Linear scaling up to all cpus has been observed on a 32-way Sun US2 box, - * 32-way Sun Niagra box, 8-way Intel box and a 4-way Power box. - * - * This class obeys the same functional specification as {@link - * Hashtable}, and includes versions of methods corresponding to - * each method of Hashtable. However, even though all operations are - * thread-safe, operations do not entail locking and there is - * not any support for locking the entire table in a way that - * prevents all access. This class is fully interoperable with - * Hashtable in programs that rely on its thread safety but not on - * its synchronization details. - * - *

Operations (including put) generally do not block, so may - * overlap with other update operations (including other puts and - * removes). Retrievals reflect the results of the most recently - * completed update operations holding upon their onset. For - * aggregate operations such as putAll, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, Iterators - * and Enumerations return elements reflecting the state of the hash table at - * some point at or since the creation of the iterator/enumeration. They do - * not throw {@link ConcurrentModificationException}. However, - * iterators are designed to be used by only one thread at a time. - * - *

Very full tables, or tables with high reprobe rates may trigger an - * internal resize operation to move into a larger table. Resizing is not - * terribly expensive, but it is not free either; during resize operations - * table throughput may drop somewhat. All threads that visit the table - * during a resize will 'help' the resizing but will still be allowed to - * complete their operation before the resize is finished (i.e., a simple - * 'get' operation on a million-entry table undergoing resizing will not need - * to block until the entire million entries are copied). - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow null to be used as a key or value. - * - * - * @since 1.5 - * @author Cliff Click - * @param the type of keys maintained by this map - * @param the type of mapped values - * - * @version 1.1.2 - * @author Prashant Deva - moved hash() function out of get_impl() so it is - * not calculated multiple times. - */ - -public class NonBlockingHashtable - extends Dictionary - implements ConcurrentMap, Cloneable, Serializable { - - private static final long serialVersionUID = 1234123412341234123L; - - private static final int REPROBE_LIMIT=10; // Too many reprobes then force a table-resize - - // --- Bits to allow Unsafe access to arrays - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - private static final int _Obase = _unsafe.arrayBaseOffset(Object[].class); - private static final int _Oscale = _unsafe.arrayIndexScale(Object[].class); - private static long rawIndex(final Object[] ary, final int idx) { - assert idx >= 0 && idx < ary.length; - return _Obase + idx * _Oscale; - } - - // --- Setup to use Unsafe - private static final long _kvs_offset; - static { // - Field f = null; - try { f = NonBlockingHashtable.class.getDeclaredField("_kvs"); } - catch( NoSuchFieldException e ) { throw new RuntimeException(e); } - _kvs_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS_kvs( final Object[] oldkvs, final Object[] newkvs ) { - return _unsafe.compareAndSwapObject(this, _kvs_offset, oldkvs, newkvs ); - } - - // --- Adding a 'prime' bit onto Values via wrapping with a junk wrapper class - private static final class Prime { - final Object _V; - Prime( Object V ) { _V = V; } - static Object unbox( Object V ) { return V instanceof Prime ? ((Prime)V)._V : V; } - } - - // --- hash ---------------------------------------------------------------- - // Helper function to spread lousy hashCodes - private static final int hash(final Object key) { - int h = key.hashCode(); // The real hashCode call - // Spread bits to regularize both segment and index locations, - // using variant of single-word Wang/Jenkins hash. - h += (h << 15) ^ 0xffffcd7d; - h ^= (h >>> 10); - h += (h << 3); - h ^= (h >>> 6); - h += (h << 2) + (h << 14); - return h ^ (h >>> 16); - } - - // --- The Hash Table -------------------- - // Slot 0 is always used for a 'CHM' entry below to hold the interesting - // bits of the hash table. Slot 1 holds full hashes as an array of ints. - // Slots {2,3}, {4,5}, etc hold {Key,Value} pairs. The entire hash table - // can be atomically replaced by CASing the _kvs field. - // - // Why is CHM buried inside the _kvs Object array, instead of the other way - // around? The CHM info is used during resize events and updates, but not - // during standard 'get' operations. I assume 'get' is much more frequent - // than 'put'. 'get' can skip the extra indirection of skipping through the - // CHM to reach the _kvs array. - private transient Object[] _kvs; - private static final CHM chm (Object[] kvs) { return (CHM )kvs[0]; } - private static final int[] hashes(Object[] kvs) { return (int[])kvs[1]; } - // Number of K,V pairs in the table - private static final int len(Object[] kvs) { return (kvs.length-2)>>1; } - - // Time since last resize - private transient long _last_resize_milli; - - // --- Minimum table size ---------------- - // Pick size 8 K/V pairs, which turns into (8*2+2)*4+12 = 84 bytes on a - // standard 32-bit HotSpot, and (8*2+2)*8+12 = 156 bytes on 64-bit Azul. - private static final int MIN_SIZE_LOG=3; // - private static final int MIN_SIZE=(1<>2); - } - - // --- NonBlockingHashtable -------------------------------------------------- - // Constructors - - /** Create a new NonBlockingHashtable with default minimum size (currently set - * to 8 K/V pairs or roughly 84 bytes on a standard 32-bit JVM). */ - public NonBlockingHashtable( ) { this(MIN_SIZE); } - - /** Create a new NonBlockingHashtable with initial room for the given number of - * elements, thus avoiding internal resizing operations to reach an - * appropriate size. Large numbers here when used with a small count of - * elements will sacrifice space for a small amount of time gained. The - * initial size will be rounded up internally to the next larger power of 2. */ - public NonBlockingHashtable( final int initial_sz ) { initialize(initial_sz); } - private final void initialize( int initial_sz ) { - if( initial_sz < 0 ) throw new IllegalArgumentException(); - int i; // Convert to next largest power-of-2 - if( initial_sz > 1024*1024 ) initial_sz = 1024*1024; - for( i=MIN_SIZE_LOG; (1<size() == 0. - * @return size() == 0 */ - @Override - public boolean isEmpty ( ) { return size() == 0; } - - /** Tests if the key in the table using the equals method. - * @return true if the key is in the table using the equals method - * @throws NullPointerException if the specified key is null */ - public boolean containsKey( Object key ) { return get(key) != null; } - - /** Legacy method testing if some key maps into the specified value in this - * table. This method is identical in functionality to {@link - * #containsValue}, and exists solely to ensure full compatibility with - * class {@link Hashtable}, which supported this method prior to - * introduction of the Java Collections framework. - * @param val a value to search for - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean contains ( Object val ) { return containsValue(val); } - - /** Maps the specified key to the specified value in the table. Neither key - * nor value can be null. - *

The value can be retrieved by calling {@link #get} with a key that is - * equal to the original key. - * @param key key with which the specified value is to be associated - * @param val value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key or value is null */ - @Override - public TypeV put ( TypeK key, TypeV val ) { return putIfMatch( key, val, NO_MATCH_OLD); } - - /** Atomically, do a {@link #put} if-and-only-if the key is not mapped. - * Useful to ensure that only a single mapping for the key exists, even if - * many threads are trying to create the mapping in parallel. - * @return the previous value associated with the specified key, - * or null if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null */ - public TypeV putIfAbsent( TypeK key, TypeV val ) { return putIfMatch( key, val, TOMBSTONE ); } - - /** Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key is null */ - @Override - public TypeV remove ( Object key ) { return putIfMatch( key,TOMBSTONE, NO_MATCH_OLD); } - - /** Atomically do a {@link #remove(Object)} if-and-only-if the key is mapped - * to a value which is equals to the given value. - * @throws NullPointerException if the specified key or value is null */ - public boolean remove ( Object key,Object val ) { return putIfMatch( key,TOMBSTONE, val ) == val; } - - /** Atomically do a put(key,val) if-and-only-if the key is - * mapped to some value already. - * @throws NullPointerException if the specified key or value is null */ - public TypeV replace ( TypeK key, TypeV val ) { return putIfMatch( key, val,MATCH_ANY ); } - - /** Atomically do a put(key,newValue) if-and-only-if the key is - * mapped a value which is equals to oldValue. - * @throws NullPointerException if the specified key or value is null */ - public boolean replace ( TypeK key, TypeV oldValue, TypeV newValue ) { - return putIfMatch( key, newValue, oldValue ) == oldValue; - } - - private final TypeV putIfMatch( Object key, Object newVal, Object oldVal ) { - if (oldVal == null || newVal == null) throw new NullPointerException(); - final Object res = putIfMatch( this, _kvs, key, newVal, oldVal ); - assert !(res instanceof Prime); - assert res != null; - return res == TOMBSTONE ? null : (TypeV)res; - } - - - /** Copies all of the mappings from the specified map to this one, replacing - * any existing mappings. - * @param m mappings to be stored in this map */ - public void putAll(Map m) { - for (Entry e : m.entrySet()) - put(e.getKey(), e.getValue()); - } - - /** Removes all of the mappings from this map. */ - public void clear() { // Smack a new empty table down - Object[] newkvs = new NonBlockingHashtable(MIN_SIZE)._kvs; - while( !CAS_kvs(_kvs,newkvs) ) // Spin until the clear works - ; - } - - /** Returns true if this Map maps one or more keys to the specified - * value. Note: This method requires a full internal traversal of the - * hash table and is much slower than {@link #containsKey}. - * @param val value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean containsValue( final Object val ) { - if( val == null ) throw new NullPointerException(); - for( TypeV V : values() ) - if( V == val || V.equals(val) ) - return true; - return false; - } - - // This function is supposed to do something for Hashtable, and the JCK - // tests hang until it gets called... by somebody ... for some reason, - // any reason.... - protected void rehash() { - } - - /** - * Creates a shallow copy of this hashtable. All the structure of the - * hashtable itself is copied, but the keys and values are not cloned. - * This is a relatively expensive operation. - * - * @return a clone of the hashtable. - */ - @Override - public Object clone() { - try { - // Must clone, to get the class right; NBHM might have been - // extended so it would be wrong to just make a new NBHM. - NonBlockingHashtable t = (NonBlockingHashtable) super.clone(); - // But I don't have an atomic clone operation - the underlying _kvs - // structure is undergoing rapid change. If I just clone the _kvs - // field, the CHM in _kvs[0] won't be in sync. - // - // Wipe out the cloned array (it was shallow anyways). - t.clear(); - // Now copy sanely - for( TypeK K : keySet() ) { - final TypeV V = get(K); // Do an official 'get' - t.put(K,V); - } - return t; - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Returns a string representation of this map. The string representation - * consists of a list of key-value mappings in the order returned by the - * map's entrySet view's iterator, enclosed in braces - * ("{}"). Adjacent mappings are separated by the characters - * ", " (comma and space). Each key-value mapping is rendered as - * the key followed by an equals sign ("=") followed by the - * associated value. Keys and values are converted to strings as by - * {@link String#valueOf(Object)}. - * - * @return a string representation of this map - */ - @Override - public String toString() { - Iterator> i = entrySet().iterator(); - if( !i.hasNext()) - return "{}"; - - StringBuilder sb = new StringBuilder(); - sb.append('{'); - for (;;) { - Entry e = i.next(); - TypeK key = e.getKey(); - TypeV value = e.getValue(); - sb.append(key == this ? "(this Map)" : key); - sb.append('='); - sb.append(value == this ? "(this Map)" : value); - if( !i.hasNext()) - return sb.append('}').toString(); - sb.append(", "); - } - } - - // --- keyeq --------------------------------------------------------------- - // Check for key equality. Try direct pointer compare first, then see if - // the hashes are unequal (fast negative test) and finally do the full-on - // 'equals' v-call. - private static boolean keyeq( Object K, Object key, int[] hashes, int hash, int fullhash ) { - return - K==key || // Either keys match exactly OR - // hash exists and matches? hash can be zero during the install of a - // new key/value pair. - ((hashes[hash] == 0 || hashes[hash] == fullhash) && - // Do not call the users' "equals()" call with a Tombstone, as this can - // surprise poorly written "equals()" calls that throw exceptions - // instead of simply returning false. - K != TOMBSTONE && // Do not call users' equals call with a Tombstone - // Do the match the hard way - with the users' key being the loop- - // invariant "this" pointer. I could have flipped the order of - // operands (since equals is commutative), but I'm making mega-morphic - // v-calls in a reprobing loop and nailing down the 'this' argument - // gives both the JIT and the hardware a chance to prefetch the call target. - key.equals(K)); // Finally do the hard match - } - - // --- get ----------------------------------------------------------------- - /** Returns the value to which the specified key is mapped, or {@code null} - * if this map contains no mapping for the key. - *

More formally, if this map contains a mapping from a key {@code k} to - * a value {@code v} such that {@code key.equals(k)}, then this method - * returns {@code v}; otherwise it returns {@code null}. (There can be at - * most one such mapping.) - * @throws NullPointerException if the specified key is null */ - // Never returns a Prime nor a Tombstone. - @Override - public TypeV get( Object key ) { - final int fullhash= hash (key); // throws NullPointerException if key is null - final Object V = get_impl(this,_kvs,key,fullhash); - assert !(V instanceof Prime); // Never return a Prime - return (TypeV)V; - } - - private static final Object get_impl( final NonBlockingHashtable topmap, final Object[] kvs, final Object key, final int fullhash ) { - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // The CHM, for a volatile read below; reads slot 0 of kvs - final int[] hashes=hashes(kvs); // The memoized hashes; reads slot 1 of kvs - - int idx = fullhash & (len-1); // First key hash - - // Main spin/reprobe loop, looking for a Key hit - int reprobe_cnt=0; - while( true ) { - // Probe table. Each read of 'val' probably misses in cache in a big - // table; hopefully the read of 'key' then hits in cache. - final Object K = key(kvs,idx); // Get key before volatile read, could be null - final Object V = val(kvs,idx); // Get value before volatile read, could be null or Tombstone or Prime - if( K == null ) return null; // A clear miss - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - // . - // We also need a volatile-read between reading a newly inserted Value - // and returning the Value (so the user might end up reading the stale - // Value contents). Same problem as with keys - and the one volatile - // read covers both. - final Object[] newkvs = chm._newkvs; // VOLATILE READ before key compare - - // Key-compare - if( keyeq(K,key,hashes,idx,fullhash) ) { - // Key hit! Check for no table-copy-in-progress - if( !(V instanceof Prime) ) // No copy? - return (V == TOMBSTONE) ? null : V; // Return the value - // Key hit - but slot is (possibly partially) copied to the new table. - // Finish the copy & retry in the new table. - return get_impl(topmap,chm.copy_slot_and_check(topmap,kvs,idx,key),key,fullhash); // Retry in the new table - } - // get and put must have the same key lookup logic! But only 'put' - // needs to force a table-resize for a too-long key-reprobe sequence. - // Check for too-many-reprobes on get - and flip to the new table. - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes - key == TOMBSTONE ) // found a TOMBSTONE key, means no more keys in this table - return newkvs == null ? null : get_impl(topmap,topmap.help_copy(newkvs),key,fullhash); // Retry in the new table - - idx = (idx+1)&(len-1); // Reprobe by 1! (could now prefetch) - } - } - - // --- putIfMatch --------------------------------------------------------- - // Put, Remove, PutIfAbsent, etc. Return the old value. If the returned - // value is equal to expVal (or expVal is NO_MATCH_OLD) then the put can be - // assumed to work (although might have been immediately overwritten). Only - // the path through copy_slot passes in an expected value of null, and - // putIfMatch only returns a null if passed in an expected null. - private static final Object putIfMatch( final NonBlockingHashtable topmap, final Object[] kvs, final Object key, final Object putval, final Object expVal ) { - assert putval != null; - assert !(putval instanceof Prime); - assert !(expVal instanceof Prime); - final int fullhash = hash (key); // throws NullPointerException if key null - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // Reads kvs[0] - final int[] hashes = hashes(kvs); // Reads kvs[1], read before kvs[0] - int idx = fullhash & (len-1); - - // --- - // Key-Claim stanza: spin till we can claim a Key (or force a resizing). - int reprobe_cnt=0; - Object K=null, V=null; - Object[] newkvs=null; - while( true ) { // Spin till we get a Key slot - V = val(kvs,idx); // Get old value (before volatile read below!) - K = key(kvs,idx); // Get current key - if( K == null ) { // Slot is free? - // Found an empty Key slot - which means this Key has never been in - // this table. No need to put a Tombstone - the Key is not here! - if( putval == TOMBSTONE ) return putval; // Not-now & never-been in this table - // Claim the null key-slot - if( CAS_key(kvs,idx, null, key ) ) { // Claim slot for Key - chm._slots.add(1); // Raise key-slots-used count - hashes[idx] = fullhash; // Memoize fullhash - break; // Got it! - } - // CAS to claim the key-slot failed. - // - // This re-read of the Key points out an annoying short-coming of Java - // CAS. Most hardware CAS's report back the existing value - so that - // if you fail you have a *witness* - the value which caused the CAS - // to fail. The Java API turns this into a boolean destroying the - // witness. Re-reading does not recover the witness because another - // thread can write over the memory after the CAS. Hence we can be in - // the unfortunate situation of having a CAS fail *for cause* but - // having that cause removed by a later store. This turns a - // non-spurious-failure CAS (such as Azul has) into one that can - // apparently spuriously fail - and we avoid apparent spurious failure - // by not allowing Keys to ever change. - K = key(kvs,idx); // CAS failed, get updated value - assert K != null; // If keys[idx] is null, CAS shoulda worked - } - // Key slot was not null, there exists a Key here - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - newkvs = chm._newkvs; // VOLATILE READ before key compare - - if( keyeq(K,key,hashes,idx,fullhash) ) - break; // Got it! - - // get and put must have the same key lookup logic! Lest 'get' give - // up looking too soon. - //topmap._reprobes.add(1); - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes or - key == TOMBSTONE ) { // found a TOMBSTONE key, means no more keys - // We simply must have a new table to do a 'put'. At this point a - // 'get' will also go to the new table (if any). We do not need - // to claim a key slot (indeed, we cannot find a free one to claim!). - newkvs = chm.resize(topmap,kvs); - if( expVal != null ) topmap.help_copy(newkvs); // help along an existing copy - return putIfMatch(topmap,newkvs,key,putval,expVal); - } - - idx = (idx+1)&(len-1); // Reprobe! - } // End of spinning till we get a Key slot - - // --- - // Found the proper Key slot, now update the matching Value slot. We - // never put a null, so Value slots monotonically move from null to - // not-null (deleted Values use Tombstone). Thus if 'V' is null we - // fail this fast cutout and fall into the check for table-full. - if( putval == V ) return V; // Fast cutout for no-change - - // See if we want to move to a new table (to avoid high average re-probe - // counts). We only check on the initial set of a Value from null to - // not-null (i.e., once per key-insert). Of course we got a 'free' check - // of newkvs once per key-compare (not really free, but paid-for by the - // time we get here). - if( newkvs == null && // New table-copy already spotted? - // Once per fresh key-insert check the hard way - ((V == null && chm.tableFull(reprobe_cnt,len)) || - // Or we found a Prime, but the JMM allowed reordering such that we - // did not spot the new table (very rare race here: the writing - // thread did a CAS of _newkvs then a store of a Prime. This thread - // reads the Prime, then reads _newkvs - but the read of Prime was so - // delayed (or the read of _newkvs was so accelerated) that they - // swapped and we still read a null _newkvs. The resize call below - // will do a CAS on _newkvs forcing the read. - V instanceof Prime) ) - newkvs = chm.resize(topmap,kvs); // Force the new table copy to start - // See if we are moving to a new table. - // If so, copy our slot and retry in the new table. - if( newkvs != null ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - - // --- - // We are finally prepared to update the existing table - while( true ) { - assert !(V instanceof Prime); - - // Must match old, and we do not? Then bail out now. Note that either V - // or expVal might be TOMBSTONE. Also V can be null, if we've never - // inserted a value before. expVal can be null if we are called from - // copy_slot. - - if( expVal != NO_MATCH_OLD && // Do we care about expected-Value at all? - V != expVal && // No instant match already? - (expVal != MATCH_ANY || V == TOMBSTONE || V == null) && - !(V==null && expVal == TOMBSTONE) && // Match on null/TOMBSTONE combo - (expVal == null || !expVal.equals(V)) ) // Expensive equals check at the last - return V; // Do not update! - - // Actually change the Value in the Key,Value pair - if( CAS_val(kvs, idx, V, putval ) ) { - // CAS succeeded - we did the update! - // Both normal put's and table-copy calls putIfMatch, but table-copy - // does not (effectively) increase the number of live k/v pairs. - if( expVal != null ) { - // Adjust sizes - a striped counter - if( (V == null || V == TOMBSTONE) && putval != TOMBSTONE ) chm._size.add( 1); - if( !(V == null || V == TOMBSTONE) && putval == TOMBSTONE ) chm._size.add(-1); - } - return (V==null && expVal!=null) ? TOMBSTONE : V; - } - // Else CAS failed - V = val(kvs,idx); // Get new value - // If a Prime'd value got installed, we need to re-run the put on the - // new table. Otherwise we lost the CAS to another racing put. - // Simply retry from the start. - if( V instanceof Prime ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - } - } - - // --- help_copy --------------------------------------------------------- - // Help along an existing resize operation. This is just a fast cut-out - // wrapper, to encourage inlining for the fast no-copy-in-progress case. We - // always help the top-most table copy, even if there are nested table - // copies in progress. - private final Object[] help_copy( Object[] helper ) { - // Read the top-level KVS only once. We'll try to help this copy along, - // even if it gets promoted out from under us (i.e., the copy completes - // and another KVS becomes the top-level copy). - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) return helper; // No copy in-progress - topchm.help_copy_impl(this,topkvs,false); - return helper; - } - - - // --- CHM ----------------------------------------------------------------- - // The control structure for the NonBlockingHashtable - private static final class CHM { - // Size in active K,V pairs - private final Counter _size; - public int size () { return (int)_size.get(); } - - // --- - // These next 2 fields are used in the resizing heuristics, to judge when - // it is time to resize or copy the table. Slots is a count of used-up - // key slots, and when it nears a large fraction of the table we probably - // end up reprobing too much. Last-resize-milli is the time since the - // last resize; if we are running back-to-back resizes without growing - // (because there are only a few live keys but many slots full of dead - // keys) then we need a larger table to cut down on the churn. - - // Count of used slots, to tell when table is full of dead unusable slots - private final Counter _slots; - public int slots() { return (int)_slots.get(); } - - // --- - // New mappings, used during resizing. - // The 'new KVs' array - created during a resize operation. This - // represents the new table being copied from the old one. It's the - // volatile variable that is read as we cross from one table to the next, - // to get the required memory orderings. It monotonically transits from - // null to set (once). - volatile Object[] _newkvs; - private final AtomicReferenceFieldUpdater _newkvsUpdater = - AtomicReferenceFieldUpdater.newUpdater(CHM.class,Object[].class, "_newkvs"); - // Set the _next field if we can. - boolean CAS_newkvs( Object[] newkvs ) { - while( _newkvs == null ) - if( _newkvsUpdater.compareAndSet(this,null,newkvs) ) - return true; - return false; - } - // Sometimes many threads race to create a new very large table. Only 1 - // wins the race, but the losers all allocate a junk large table with - // hefty allocation costs. Attempt to control the overkill here by - // throttling attempts to create a new table. I cannot really block here - // (lest I lose the non-blocking property) but late-arriving threads can - // give the initial resizing thread a little time to allocate the initial - // new table. The Right Long Term Fix here is to use array-lets and - // incrementally create the new very large array. In C I'd make the array - // with malloc (which would mmap under the hood) which would only eat - // virtual-address and not real memory - and after Somebody wins then we - // could in parallel initialize the array. Java does not allow - // un-initialized array creation (especially of ref arrays!). - volatile long _resizers; // count of threads attempting an initial resize - private static final AtomicLongFieldUpdater _resizerUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_resizers"); - - // --- - // Simple constructor - CHM( Counter size ) { - _size = size; - _slots= new Counter(); - } - - // --- tableFull --------------------------------------------------------- - // Heuristic to decide if this table is too full, and we should start a - // new table. Note that if a 'get' call has reprobed too many times and - // decided the table must be full, then always the estimate_sum must be - // high and we must report the table is full. If we do not, then we might - // end up deciding that the table is not full and inserting into the - // current table, while a 'get' has decided the same key cannot be in this - // table because of too many reprobes. The invariant is: - // slots.estimate_sum >= max_reprobe_cnt >= reprobe_limit(len) - private final boolean tableFull( int reprobe_cnt, int len ) { - return - // Do the cheap check first: we allow some number of reprobes always - reprobe_cnt >= REPROBE_LIMIT && - // More expensive check: see if the table is > 1/4 full. - _slots.estimate_get() >= reprobe_limit(len); - } - - // --- resize ------------------------------------------------------------ - // Resizing after too many probes. "How Big???" heuristics are here. - // Callers will (not this routine) will 'help_copy' any in-progress copy. - // Since this routine has a fast cutout for copy-already-started, callers - // MUST 'help_copy' lest we have a path which forever runs through - // 'resize' only to discover a copy-in-progress which never progresses. - private final Object[] resize( NonBlockingHashtable topmap, Object[] kvs) { - assert chm(kvs) == this; - - // Check for resize already in progress, probably triggered by another thread - Object[] newkvs = _newkvs; // VOLATILE READ - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // No copy in-progress, so start one. First up: compute new table size. - int oldlen = len(kvs); // Old count of K,V pairs allowed - int sz = size(); // Get current table count of active K,V pairs - int newsz = sz; // First size estimate - - // Heuristic to determine new size. We expect plenty of dead-slots-with-keys - // and we need some decent padding to avoid endless reprobing. - if( sz >= (oldlen>>2) ) { // If we are >25% full of keys then... - newsz = oldlen<<1; // Double size - if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - newsz = oldlen<<2; // Double double size - } - // This heuristic in the next 2 lines leads to a much denser table - // with a higher reprobe rate - //if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - // newsz = oldlen<<1; // Double size - - // Last (re)size operation was very recent? Then double again; slows - // down resize operations for tables subject to a high key churn rate. - long tm = System.currentTimeMillis(); - long q=0; - if( newsz <= oldlen && // New table would shrink or hold steady? - tm <= topmap._last_resize_milli+10000 && // Recent resize (less than 1 sec ago) - (q=_slots.estimate_get()) >= (sz<<1) ) // 1/2 of keys are dead? - newsz = oldlen<<1; // Double the existing size - - // Do not shrink, ever - if( newsz < oldlen ) newsz = oldlen; - - // Convert to power-of-2 - int log2; - for( log2=MIN_SIZE_LOG; (1<>20/*megs*/; - if( r >= 2 && megs > 0 ) { // Already 2 guys trying; wait and see - newkvs = _newkvs; // Between dorking around, another thread did it - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - // TODO - use a wait with timeout, so we'll wakeup as soon as the new table - // is ready, or after the timeout in any case. - //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup - // For now, sleep a tad and see if the 2 guys already trying to make - // the table actually get around to making it happen. - try { Thread.sleep(8*megs); } catch( Exception e ) { } - } - // Last check, since the 'new' below is expensive and there is a chance - // that another thread slipped in a new thread while we ran the heuristic. - newkvs = _newkvs; - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // Double size for K,V pairs, add 1 for CHM - newkvs = new Object[((1< _copyIdxUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyIdx"); - - // Work-done reporting. Used to efficiently signal when we can move to - // the new table. From 0 to len(oldkvs) refers to copying from the old - // table to the new. - volatile long _copyDone= 0; - static private final AtomicLongFieldUpdater _copyDoneUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyDone"); - - // --- help_copy_impl ---------------------------------------------------- - // Help along an existing resize operation. We hope its the top-level - // copy (it was when we started) but this CHM might have been promoted out - // of the top position. - private final void help_copy_impl( NonBlockingHashtable topmap, Object[] oldkvs, boolean copy_all ) { - assert chm(oldkvs) == this; - Object[] newkvs = _newkvs; - assert newkvs != null; // Already checked by caller - int oldlen = len(oldkvs); // Total amount to copy - final int MIN_COPY_WORK = Math.min(oldlen,1024); // Limit per-thread work - - // --- - int panic_start = -1; - int copyidx=-9999; // Fool javac to think it's initialized - while( _copyDone < oldlen ) { // Still needing to copy? - // Carve out a chunk of work. The counter wraps around so every - // thread eventually tries to copy every slot repeatedly. - - // We "panic" if we have tried TWICE to copy every slot - and it still - // has not happened. i.e., twice some thread somewhere claimed they - // would copy 'slot X' (by bumping _copyIdx) but they never claimed to - // have finished (by bumping _copyDone). Our choices become limited: - // we can wait for the work-claimers to finish (and become a blocking - // algorithm) or do the copy work ourselves. Tiny tables with huge - // thread counts trying to copy the table often 'panic'. - if( panic_start == -1 ) { // No panic? - copyidx = (int)_copyIdx; - while( copyidx < (oldlen<<1) && // 'panic' check - !_copyIdxUpdater.compareAndSet(this,copyidx,copyidx+MIN_COPY_WORK) ) - copyidx = (int)_copyIdx; // Re-read - if( !(copyidx < (oldlen<<1)) ) // Panic! - panic_start = copyidx; // Record where we started to panic-copy - } - - // We now know what to copy. Try to copy. - int workdone = 0; - for( int i=0; i 0 ) // Report work-done occasionally - copy_check_and_promote( topmap, oldkvs, workdone );// See if we can promote - //for( int i=0; i 0 ) { - while( !_copyDoneUpdater.compareAndSet(this,copyDone,copyDone+workdone) ) { - copyDone = _copyDone; // Reset, retry - assert (copyDone+workdone) <= oldlen; - } - //if( (10*copyDone/oldlen) != (10*(copyDone+workdone)/oldlen) ) - //System.out.print(" "+(copyDone+workdone)*100/oldlen+"%"+"_"+(_copyIdx*100/oldlen)+"%"); - } - - // Check for copy being ALL done, and promote. Note that we might have - // nested in-progress copies and manage to finish a nested copy before - // finishing the top-level copy. We only promote top-level copies. - if( copyDone+workdone == oldlen && // Ready to promote this table? - topmap._kvs == oldkvs && // Looking at the top-level table? - // Attempt to promote - topmap.CAS_kvs(oldkvs,_newkvs) ) { - topmap._last_resize_milli = System.currentTimeMillis(); // Record resize time for next check - //long nano = System.nanoTime(); - //System.out.println(" "+nano+" Promote table to "+len(_newkvs)); - //if( System.out != null ) System.out.print("]"); - } - } - - // --- copy_slot --------------------------------------------------------- - // Copy one K/V pair from oldkvs[i] to newkvs. Returns true if we can - // confirm that the new table guaranteed has a value for this old-table - // slot. We need an accurate confirmed-copy count so that we know when we - // can promote (if we promote the new table too soon, other threads may - // 'miss' on values not-yet-copied from the old table). We don't allow - // any direct updates on the new table, unless they first happened to the - // old table - so that any transition in the new table from null to - // not-null must have been from a copy_slot (or other old-table overwrite) - // and not from a thread directly writing in the new table. Thus we can - // count null-to-not-null transitions in the new table. - private boolean copy_slot( NonBlockingHashtable topmap, int idx, Object[] oldkvs, Object[] newkvs ) { - // Blindly set the key slot from null to TOMBSTONE, to eagerly stop - // fresh put's from inserting new values in the old table when the old - // table is mid-resize. We don't need to act on the results here, - // because our correctness stems from box'ing the Value field. Slamming - // the Key field is a minor speed optimization. - Object key; - while( (key=key(oldkvs,idx)) == null ) - CAS_key(oldkvs,idx, null, TOMBSTONE); - - // --- - // Prevent new values from appearing in the old table. - // Box what we see in the old table, to prevent further updates. - Object oldval = val(oldkvs,idx); // Read OLD table - while( !(oldval instanceof Prime) ) { - final Prime box = (oldval == null || oldval == TOMBSTONE) ? TOMBPRIME : new Prime(oldval); - if( CAS_val(oldkvs,idx,oldval,box) ) { // CAS down a box'd version of oldval - // If we made the Value slot hold a TOMBPRIME, then we both - // prevented further updates here but also the (absent) - // oldval is vaccuously available in the new table. We - // return with true here: any thread looking for a value for - // this key can correctly go straight to the new table and - // skip looking in the old table. - if( box == TOMBPRIME ) - return true; - // Otherwise we boxed something, but it still needs to be - // copied into the new table. - oldval = box; // Record updated oldval - break; // Break loop; oldval is now boxed by us - } - oldval = val(oldkvs,idx); // Else try, try again - } - if( oldval == TOMBPRIME ) return false; // Copy already complete here! - - // --- - // Copy the value into the new table, but only if we overwrite a null. - // If another value is already in the new table, then somebody else - // wrote something there and that write is happens-after any value that - // appears in the old table. If putIfMatch does not find a null in the - // new table - somebody else should have recorded the null-not_null - // transition in this copy. - Object old_unboxed = ((Prime)oldval)._V; - assert old_unboxed != TOMBSTONE; - boolean copied_into_new = (putIfMatch(topmap, newkvs, key, old_unboxed, null) == null); - - // --- - // Finally, now that any old value is exposed in the new table, we can - // forever hide the old-table value by slapping a TOMBPRIME down. This - // will stop other threads from uselessly attempting to copy this slot - // (i.e., it's a speed optimization not a correctness issue). - while( !CAS_val(oldkvs,idx,oldval,TOMBPRIME) ) - oldval = val(oldkvs,idx); - - return copied_into_new; - } // end copy_slot - } // End of CHM - - - // --- Snapshot ------------------------------------------------------------ - // The main class for iterating over the NBHM. It "snapshots" a clean - // view of the K/V array. - private class SnapshotV implements Iterator, Enumeration { - final Object[] _sskvs; - public SnapshotV() { - while( true ) { // Verify no table-copy-in-progress - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) { // No table-copy-in-progress - // The "linearization point" for the iteration. Every key in this - // table will be visited, but keys added later might be skipped or - // even be added to a following table (also not iterated over). - _sskvs = topkvs; - break; - } - // Table copy in-progress - so we cannot get a clean iteration. We - // must help finish the table copy before we can start iterating. - topchm.help_copy_impl(NonBlockingHashtable.this,topkvs,true); - } - // Warm-up the iterator - next(); - } - int length() { return len(_sskvs); } - Object key(int idx) { return NonBlockingHashtable.key(_sskvs,idx); } - private int _idx; // Varies from 0-keys.length - private Object _nextK, _prevK; // Last 2 keys found - private TypeV _nextV, _prevV; // Last 2 values found - public boolean hasNext() { return _nextV != null; } - public TypeV next() { - // 'next' actually knows what the next value will be - it had to - // figure that out last go-around lest 'hasNext' report true and - // some other thread deleted the last value. Instead, 'next' - // spends all its effort finding the key that comes after the - // 'next' key. - if( _idx != 0 && _nextV == null ) throw new NoSuchElementException(); - _prevK = _nextK; // This will become the previous key - _prevV = _nextV; // This will become the previous value - _nextV = null; // We have no more next-key - // Attempt to set <_nextK,_nextV> to the next K,V pair. - // _nextV is the trigger: stop searching when it is != null - while( _idx elements() { return new SnapshotV(); } - - // --- values -------------------------------------------------------------- - /** Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are reflected - * in the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - public Collection values() { - return new AbstractCollection() { - @Override public void clear ( ) { NonBlockingHashtable.this.clear ( ); } - @Override public int size ( ) { return NonBlockingHashtable.this.size ( ); } - @Override public boolean contains( Object v ) { return NonBlockingHashtable.this.containsValue(v); } - @Override public Iterator iterator() { return new SnapshotV(); } - }; - } - - // --- keySet -------------------------------------------------------------- - private class SnapshotK implements Iterator, Enumeration { - final SnapshotV _ss; - public SnapshotK() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public TypeK next() { _ss.next(); return (TypeK)_ss._prevK; } - public boolean hasNext() { return _ss.hasNext(); } - public TypeK nextElement() { return next(); } - public boolean hasMoreElements() { return hasNext(); } - } - - /** Returns an enumeration of the keys in this table. - * @return an enumeration of the keys in this table - * @see #keySet() */ - public Enumeration keys() { return new SnapshotK(); } - - /** Returns a {@link Set} view of the keys contained in this map. The set - * is backed by the map, so changes to the map are reflected in the set, - * and vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - public Set keySet() { - return new AbstractSet () { - @Override public void clear ( ) { NonBlockingHashtable.this.clear ( ); } - @Override public int size ( ) { return NonBlockingHashtable.this.size ( ); } - @Override public boolean contains( Object k ) { return NonBlockingHashtable.this.containsKey(k); } - @Override public boolean remove ( Object k ) { return NonBlockingHashtable.this.remove (k) != null; } - @Override public Iterator iterator() { return new SnapshotK(); } - }; - } - - - // --- entrySet ------------------------------------------------------------ - // Warning: Each call to 'next' in this iterator constructs a new NBHMEntry. - private class NBHMEntry extends AbstractEntry { - NBHMEntry( final TypeK k, final TypeV v ) { super(k,v); } - public TypeV setValue(final TypeV val) { - if( val == null ) throw new NullPointerException(); - _val = val; - return put(_key, val); - } - } - - private class SnapshotE implements Iterator> { - final SnapshotV _ss; - public SnapshotE() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public Entry next() { _ss.next(); return new NBHMEntry((TypeK)_ss._prevK,_ss._prevV); } - public boolean hasNext() { return _ss.hasNext(); } - } - - /** Returns a {@link Set} view of the mappings contained in this map. The - * set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes - * the corresponding mapping from the map, via the - * Iterator.remove, Set.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - * - *

Warning: the iterator associated with this Set - * requires the creation of {@link Entry} objects with each - * iteration. The {@link NonBlockingHashtable} does not normally create or - * using {@link Entry} objects so they will be created soley - * to support this iteration. Iterating using {@link #keySet} or {@link - * #values} will be more efficient. - */ - public Set> entrySet() { - return new AbstractSet>() { - @Override public void clear ( ) { NonBlockingHashtable.this.clear( ); } - @Override public int size ( ) { return NonBlockingHashtable.this.size ( ); } - @Override public boolean remove( final Object o ) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - return NonBlockingHashtable.this.remove(e.getKey(), e.getValue()); - } - @Override public boolean contains(final Object o) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - TypeV v = get(e.getKey()); - return v.equals(e.getValue()); - } - @Override public Iterator> iterator() { return new SnapshotE(); } - }; - } - - // --- writeObject ------------------------------------------------------- - // Write a NBHM to a stream - private void writeObject(java.io.ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); // Nothing to write - for( Object K : keySet() ) { - final Object V = get(K); // Do an official 'get' - s.writeObject(K); // Write the pair - s.writeObject(V); - } - s.writeObject(null); // Sentinel to indicate end-of-data - s.writeObject(null); - } - - // --- readObject -------------------------------------------------------- - // Read a CHM from a stream - private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { - s.defaultReadObject(); // Read nothing - initialize(MIN_SIZE); - for(;;) { - final TypeK K = (TypeK) s.readObject(); - final TypeV V = (TypeV) s.readObject(); - if( K == null ) break; - put(K,V); // Insert with an offical put - } - } - -} // End NonBlockingHashtable class diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingIdentityHashMap.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingIdentityHashMap.java deleted file mode 100644 index 193e15f2b..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingIdentityHashMap.java +++ /dev/null @@ -1,1285 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.*; - -import sun.misc.Unsafe; - -/** - * A lock-free alternate implementation of {@link java.util.concurrent.ConcurrentHashMap} - * with better scaling properties and generally lower costs to mutate the Map. - * It provides identical correctness properties as ConcurrentHashMap. All - * operations are non-blocking and multi-thread safe, including all update - * operations. {@link NonBlockingHashMap} scales substatially better than - * {@link java.util.concurrent.ConcurrentHashMap} for high update rates, even with a - * large concurrency factor. Scaling is linear up to 768 CPUs on a 768-CPU - * Azul box, even with 100% updates or 100% reads or any fraction in-between. - * Linear scaling up to all cpus has been observed on a 32-way Sun US2 box, - * 32-way Sun Niagra box, 8-way Intel box and a 4-way Power box. - * - * This class obeys the same functional specification as {@link - * Hashtable}, and includes versions of methods corresponding to - * each method of Hashtable. However, even though all operations are - * thread-safe, operations do not entail locking and there is - * not any support for locking the entire table in a way that - * prevents all access. This class is fully interoperable with - * Hashtable in programs that rely on its thread safety but not on - * its synchronization details. - * - *

Operations (including put) generally do not block, so may - * overlap with other update operations (including other puts and - * removes). Retrievals reflect the results of the most recently - * completed update operations holding upon their onset. For - * aggregate operations such as putAll, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, Iterators - * and Enumerations return elements reflecting the state of the hash table at - * some point at or since the creation of the iterator/enumeration. They do - * not throw {@link ConcurrentModificationException}. However, - * iterators are designed to be used by only one thread at a time. - * - *

Very full tables, or tables with high reprobe rates may trigger an - * internal resize operation to move into a larger table. Resizing is not - * terribly expensive, but it is not free either; during resize operations - * table throughput may drop somewhat. All threads that visit the table - * during a resize will 'help' the resizing but will still be allowed to - * complete their operation before the resize is finished (i.e., a simple - * 'get' operation on a million-entry table undergoing resizing will not need - * to block until the entire million entries are copied). - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow null to be used as a key or value. - * - * - * @since 1.5 - * @author Cliff Click - * @param the type of keys maintained by this map - * @param the type of mapped values - * - * @author Prashant Deva - * Modified from original NonBlockingHashMap to use identity equality. - * Uses System.identityHashCode() to calculate hashMap. - * Key equality is compared using '=='. - */ - -public class NonBlockingIdentityHashMap - extends AbstractMap - implements ConcurrentMap, Cloneable, Serializable { - - private static final long serialVersionUID = 1234123412341234123L; - - private static final int REPROBE_LIMIT=10; // Too many reprobes then force a table-resize - - // --- Bits to allow Unsafe access to arrays - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - private static final int _Obase = _unsafe.arrayBaseOffset(Object[].class); - private static final int _Oscale = _unsafe.arrayIndexScale(Object[].class); - private static long rawIndex(final Object[] ary, final int idx) { - assert idx >= 0 && idx < ary.length; - return _Obase + idx * _Oscale; - } - - // --- Setup to use Unsafe - private static final long _kvs_offset; - static { // - Field f = null; - try { f = NonBlockingHashMap.class.getDeclaredField("_kvs"); } - catch( NoSuchFieldException e ) { throw new RuntimeException(e); } - _kvs_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS_kvs( final Object[] oldkvs, final Object[] newkvs ) { - return _unsafe.compareAndSwapObject(this, _kvs_offset, oldkvs, newkvs ); - } - - // --- Adding a 'prime' bit onto Values via wrapping with a junk wrapper class - private static final class Prime { - final Object _V; - Prime( Object V ) { _V = V; } - static Object unbox( Object V ) { return V instanceof Prime ? ((Prime)V)._V : V; } - } - - // --- hash ---------------------------------------------------------------- - // Helper function to spread lousy hashCodes - private static final int hash(final Object key) { - int h = System.identityHashCode(key); // The real hashCode call - h ^= (h>>>20) ^ (h>>>12); - h ^= (h>>> 7) ^ (h>>> 4); - return h; - } - - // --- The Hash Table -------------------- - // Slot 0 is always used for a 'CHM' entry below to hold the interesting - // bits of the hash table. Slot 1 holds full hashes as an array of ints. - // Slots {2,3}, {4,5}, etc hold {Key,Value} pairs. The entire hash table - // can be atomically replaced by CASing the _kvs field. - // - // Why is CHM buried inside the _kvs Object array, instead of the other way - // around? The CHM info is used during resize events and updates, but not - // during standard 'get' operations. I assume 'get' is much more frequent - // than 'put'. 'get' can skip the extra indirection of skipping through the - // CHM to reach the _kvs array. - private transient Object[] _kvs; - private static final CHM chm (Object[] kvs) { return (CHM )kvs[0]; } - private static final int[] hashes(Object[] kvs) { return (int[])kvs[1]; } - // Number of K,V pairs in the table - private static final int len(Object[] kvs) { return (kvs.length-2)>>1; } - - // Time since last resize - private transient long _last_resize_milli; - - // --- Minimum table size ---------------- - // Pick size 8 K/V pairs, which turns into (8*2+2)*4+12 = 84 bytes on a - // standard 32-bit HotSpot, and (8*2+2)*8+12 = 156 bytes on 64-bit Azul. - private static final int MIN_SIZE_LOG=3; // - private static final int MIN_SIZE=(1<>2); - } - - // --- NonBlockingHashMap -------------------------------------------------- - // Constructors - - /** Create a new NonBlockingHashMap with default minimum size (currently set - * to 8 K/V pairs or roughly 84 bytes on a standard 32-bit JVM). */ - public NonBlockingIdentityHashMap( ) { this(MIN_SIZE); } - - /** Create a new NonBlockingHashMap with initial room for the given number of - * elements, thus avoiding internal resizing operations to reach an - * appropriate size. Large numbers here when used with a small count of - * elements will sacrifice space for a small amount of time gained. The - * initial size will be rounded up internally to the next larger power of 2. */ - public NonBlockingIdentityHashMap( final int initial_sz ) { initialize(initial_sz); } - private final void initialize( int initial_sz ) { - if( initial_sz < 0 ) throw new IllegalArgumentException(); - int i; // Convert to next largest power-of-2 - if( initial_sz > 1024*1024 ) initial_sz = 1024*1024; - for( i=MIN_SIZE_LOG; (1<size() == 0. - * @return size() == 0 */ - @Override - public boolean isEmpty ( ) { return size() == 0; } - - /** Tests if the key in the table using the equals method. - * @return true if the key is in the table using the equals method - * @throws NullPointerException if the specified key is null */ - @Override - public boolean containsKey( Object key ) { return get(key) != null; } - - /** Legacy method testing if some key maps into the specified value in this - * table. This method is identical in functionality to {@link - * #containsValue}, and exists solely to ensure full compatibility with - * class {@link Hashtable}, which supported this method prior to - * introduction of the Java Collections framework. - * @param val a value to search for - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - public boolean contains ( Object val ) { return containsValue(val); } - - /** Maps the specified key to the specified value in the table. Neither key - * nor value can be null. - *

The value can be retrieved by calling {@link #get} with a key that is - * equal to the original key. - * @param key key with which the specified value is to be associated - * @param val value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key or value is null */ - @Override - public TypeV put ( TypeK key, TypeV val ) { return putIfMatch( key, val, NO_MATCH_OLD); } - - /** Atomically, do a {@link #put} if-and-only-if the key is not mapped. - * Useful to ensure that only a single mapping for the key exists, even if - * many threads are trying to create the mapping in parallel. - * @return the previous value associated with the specified key, - * or null if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null */ - public TypeV putIfAbsent( TypeK key, TypeV val ) { return putIfMatch( key, val, TOMBSTONE ); } - - /** Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * @return the previous value associated with key, or - * null if there was no mapping for key - * @throws NullPointerException if the specified key is null */ - @Override - public TypeV remove ( Object key ) { return putIfMatch( key,TOMBSTONE, NO_MATCH_OLD); } - - /** Atomically do a {@link #remove(Object)} if-and-only-if the key is mapped - * to a value which is equals to the given value. - * @throws NullPointerException if the specified key or value is null */ - public boolean remove ( Object key,Object val ) { return putIfMatch( key,TOMBSTONE, val ) == val; } - - /** Atomically do a put(key,val) if-and-only-if the key is - * mapped to some value already. - * @throws NullPointerException if the specified key or value is null */ - public TypeV replace ( TypeK key, TypeV val ) { return putIfMatch( key, val,MATCH_ANY ); } - - /** Atomically do a put(key,newValue) if-and-only-if the key is - * mapped a value which is equals to oldValue. - * @throws NullPointerException if the specified key or value is null */ - public boolean replace ( TypeK key, TypeV oldValue, TypeV newValue ) { - return putIfMatch( key, newValue, oldValue ) == oldValue; - } - - private final TypeV putIfMatch( Object key, Object newVal, Object oldVal ) { - if (oldVal == null || newVal == null) throw new NullPointerException(); - final Object res = putIfMatch( this, _kvs, key, newVal, oldVal ); - assert !(res instanceof Prime); - assert res != null; - return res == TOMBSTONE ? null : (TypeV)res; - } - - - /** Copies all of the mappings from the specified map to this one, replacing - * any existing mappings. - * @param m mappings to be stored in this map */ - @Override - public void putAll(Map m) { - for (Entry e : m.entrySet()) - put(e.getKey(), e.getValue()); - } - - /** Removes all of the mappings from this map. */ - @Override - public void clear() { // Smack a new empty table down - Object[] newkvs = new NonBlockingIdentityHashMap(MIN_SIZE)._kvs; - while( !CAS_kvs(_kvs,newkvs) ) // Spin until the clear works - ; - } - - /** Returns true if this Map maps one or more keys to the specified - * value. Note: This method requires a full internal traversal of the - * hash table and is much slower than {@link #containsKey}. - * @param val value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null */ - @Override - public boolean containsValue( final Object val ) { - if( val == null ) throw new NullPointerException(); - for( TypeV V : values() ) - if( V == val || V.equals(val) ) - return true; - return false; - } - - // This function is supposed to do something for Hashtable, and the JCK - // tests hang until it gets called... by somebody ... for some reason, - // any reason.... - protected void rehash() { - } - - /** - * Creates a shallow copy of this hashtable. All the structure of the - * hashtable itself is copied, but the keys and values are not cloned. - * This is a relatively expensive operation. - * - * @return a clone of the hashtable. - */ - @Override - public Object clone() { - try { - // Must clone, to get the class right; NBHM might have been - // extended so it would be wrong to just make a new NBHM. - NonBlockingIdentityHashMap t = (NonBlockingIdentityHashMap) super.clone(); - // But I don't have an atomic clone operation - the underlying _kvs - // structure is undergoing rapid change. If I just clone the _kvs - // field, the CHM in _kvs[0] won't be in sync. - // - // Wipe out the cloned array (it was shallow anyways). - t.clear(); - // Now copy sanely - for( TypeK K : keySet() ) { - final TypeV V = get(K); // Do an official 'get' - t.put(K,V); - } - return t; - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Returns a string representation of this map. The string representation - * consists of a list of key-value mappings in the order returned by the - * map's entrySet view's iterator, enclosed in braces - * ("{}"). Adjacent mappings are separated by the characters - * ", " (comma and space). Each key-value mapping is rendered as - * the key followed by an equals sign ("=") followed by the - * associated value. Keys and values are converted to strings as by - * {@link String#valueOf(Object)}. - * - * @return a string representation of this map - */ - @Override - public String toString() { - Iterator> i = entrySet().iterator(); - if( !i.hasNext()) - return "{}"; - - StringBuilder sb = new StringBuilder(); - sb.append('{'); - for (;;) { - Entry e = i.next(); - TypeK key = e.getKey(); - TypeV value = e.getValue(); - sb.append(key == this ? "(this Map)" : key); - sb.append('='); - sb.append(value == this ? "(this Map)" : value); - if( !i.hasNext()) - return sb.append('}').toString(); - sb.append(", "); - } - } - - // --- get ----------------------------------------------------------------- - /** Returns the value to which the specified key is mapped, or {@code null} - * if this map contains no mapping for the key. - *

More formally, if this map contains a mapping from a key {@code k} to - * a value {@code v} such that {@code key.equals(k)}, then this method - * returns {@code v}; otherwise it returns {@code null}. (There can be at - * most one such mapping.) - * @throws NullPointerException if the specified key is null */ - // Never returns a Prime nor a Tombstone. - @Override - public TypeV get( Object key ) { - final int fullhash= hash (key); // throws NullPointerException if key is null - final Object V = get_impl(this,_kvs,key,fullhash); - assert !(V instanceof Prime); // Never return a Prime - return (TypeV)V; - } - - private static final Object get_impl( final NonBlockingIdentityHashMap topmap, final Object[] kvs, final Object key, final int fullhash ) { - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // The CHM, for a volatile read below; reads slot 0 of kvs - - int idx = fullhash & (len-1); // First key hash - - // Main spin/reprobe loop, looking for a Key hit - int reprobe_cnt=0; - while( true ) { - // Probe table. Each read of 'val' probably misses in cache in a big - // table; hopefully the read of 'key' then hits in cache. - final Object K = key(kvs,idx); // Get key before volatile read, could be null - final Object V = val(kvs,idx); // Get value before volatile read, could be null or Tombstone or Prime - if( K == null ) return null; // A clear miss - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - // . - // We also need a volatile-read between reading a newly inserted Value - // and returning the Value (so the user might end up reading the stale - // Value contents). Same problem as with keys - and the one volatile - // read covers both. - final Object[] newkvs = chm._newkvs; // VOLATILE READ before key compare - - // Key-compare - if( K == key ) { - // Key hit! Check for no table-copy-in-progress - if( !(V instanceof Prime) ) // No copy? - return (V == TOMBSTONE) ? null : V; // Return the value - // Key hit - but slot is (possibly partially) copied to the new table. - // Finish the copy & retry in the new table. - return get_impl(topmap,chm.copy_slot_and_check(topmap,kvs,idx,key),key,fullhash); // Retry in the new table - } - // get and put must have the same key lookup logic! But only 'put' - // needs to force a table-resize for a too-long key-reprobe sequence. - // Check for too-many-reprobes on get - and flip to the new table. - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes - key == TOMBSTONE ) // found a TOMBSTONE key, means no more keys in this table - return newkvs == null ? null : get_impl(topmap,topmap.help_copy(newkvs),key,fullhash); // Retry in the new table - - idx = (idx+1)&(len-1); // Reprobe by 1! (could now prefetch) - } - } - - // --- putIfMatch --------------------------------------------------------- - // Put, Remove, PutIfAbsent, etc. Return the old value. If the returned - // value is equal to expVal (or expVal is NO_MATCH_OLD) then the put can be - // assumed to work (although might have been immediately overwritten). Only - // the path through copy_slot passes in an expected value of null, and - // putIfMatch only returns a null if passed in an expected null. - private static final Object putIfMatch( final NonBlockingIdentityHashMap topmap, final Object[] kvs, final Object key, final Object putval, final Object expVal ) { - assert putval != null; - assert !(putval instanceof Prime); - assert !(expVal instanceof Prime); - final int fullhash = hash (key); // throws NullPointerException if key null - final int len = len (kvs); // Count of key/value pairs, reads kvs.length - final CHM chm = chm (kvs); // Reads kvs[0] - int idx = fullhash & (len-1); - - // --- - // Key-Claim stanza: spin till we can claim a Key (or force a resizing). - int reprobe_cnt=0; - Object K=null, V=null; - Object[] newkvs=null; - while( true ) { // Spin till we get a Key slot - V = val(kvs,idx); // Get old value (before volatile read below!) - K = key(kvs,idx); // Get current key - if( K == null ) { // Slot is free? - // Found an empty Key slot - which means this Key has never been in - // this table. No need to put a Tombstone - the Key is not here! - if( putval == TOMBSTONE ) return putval; // Not-now & never-been in this table - // Claim the null key-slot - if( CAS_key(kvs,idx, null, key ) ) { // Claim slot for Key - chm._slots.add(1); // Raise key-slots-used count - break; // Got it! - } - // CAS to claim the key-slot failed. - // - // This re-read of the Key points out an annoying short-coming of Java - // CAS. Most hardware CAS's report back the existing value - so that - // if you fail you have a *witness* - the value which caused the CAS - // to fail. The Java API turns this into a boolean destroying the - // witness. Re-reading does not recover the witness because another - // thread can write over the memory after the CAS. Hence we can be in - // the unfortunate situation of having a CAS fail *for cause* but - // having that cause removed by a later store. This turns a - // non-spurious-failure CAS (such as Azul has) into one that can - // apparently spuriously fail - and we avoid apparent spurious failure - // by not allowing Keys to ever change. - K = key(kvs,idx); // CAS failed, get updated value - assert K != null; // If keys[idx] is null, CAS shoulda worked - } - // Key slot was not null, there exists a Key here - - // We need a volatile-read here to preserve happens-before semantics on - // newly inserted Keys. If the Key body was written just before inserting - // into the table a Key-compare here might read the uninitalized Key body. - // Annoyingly this means we have to volatile-read before EACH key compare. - newkvs = chm._newkvs; // VOLATILE READ before key compare - - if( K == key ) - break; // Got it! - - // get and put must have the same key lookup logic! Lest 'get' give - // up looking too soon. - //topmap._reprobes.add(1); - if( ++reprobe_cnt >= reprobe_limit(len) || // too many probes or - key == TOMBSTONE ) { // found a TOMBSTONE key, means no more keys - // We simply must have a new table to do a 'put'. At this point a - // 'get' will also go to the new table (if any). We do not need - // to claim a key slot (indeed, we cannot find a free one to claim!). - newkvs = chm.resize(topmap,kvs); - if( expVal != null ) topmap.help_copy(newkvs); // help along an existing copy - return putIfMatch(topmap,newkvs,key,putval,expVal); - } - - idx = (idx+1)&(len-1); // Reprobe! - } // End of spinning till we get a Key slot - - // --- - // Found the proper Key slot, now update the matching Value slot. We - // never put a null, so Value slots monotonically move from null to - // not-null (deleted Values use Tombstone). Thus if 'V' is null we - // fail this fast cutout and fall into the check for table-full. - if( putval == V ) return V; // Fast cutout for no-change - - // See if we want to move to a new table (to avoid high average re-probe - // counts). We only check on the initial set of a Value from null to - // not-null (i.e., once per key-insert). Of course we got a 'free' check - // of newkvs once per key-compare (not really free, but paid-for by the - // time we get here). - if( newkvs == null && // New table-copy already spotted? - // Once per fresh key-insert check the hard way - ((V == null && chm.tableFull(reprobe_cnt,len)) || - // Or we found a Prime, but the JMM allowed reordering such that we - // did not spot the new table (very rare race here: the writing - // thread did a CAS of _newkvs then a store of a Prime. This thread - // reads the Prime, then reads _newkvs - but the read of Prime was so - // delayed (or the read of _newkvs was so accelerated) that they - // swapped and we still read a null _newkvs. The resize call below - // will do a CAS on _newkvs forcing the read. - V instanceof Prime) ) - newkvs = chm.resize(topmap,kvs); // Force the new table copy to start - // See if we are moving to a new table. - // If so, copy our slot and retry in the new table. - if( newkvs != null ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - - // --- - // We are finally prepared to update the existing table - while( true ) { - assert !(V instanceof Prime); - - // Must match old, and we do not? Then bail out now. Note that either V - // or expVal might be TOMBSTONE. Also V can be null, if we've never - // inserted a value before. expVal can be null if we are called from - // copy_slot. - - if( expVal != NO_MATCH_OLD && // Do we care about expected-Value at all? - V != expVal && // No instant match already? - (expVal != MATCH_ANY || V == TOMBSTONE || V == null) && - !(V==null && expVal == TOMBSTONE) && // Match on null/TOMBSTONE combo - (expVal == null || !expVal.equals(V)) ) // Expensive equals check at the last - return V; // Do not update! - - // Actually change the Value in the Key,Value pair - if( CAS_val(kvs, idx, V, putval ) ) { - // CAS succeeded - we did the update! - // Both normal put's and table-copy calls putIfMatch, but table-copy - // does not (effectively) increase the number of live k/v pairs. - if( expVal != null ) { - // Adjust sizes - a striped counter - if( (V == null || V == TOMBSTONE) && putval != TOMBSTONE ) chm._size.add( 1); - if( !(V == null || V == TOMBSTONE) && putval == TOMBSTONE ) chm._size.add(-1); - } - return (V==null && expVal!=null) ? TOMBSTONE : V; - } - // Else CAS failed - V = val(kvs,idx); // Get new value - // If a Prime'd value got installed, we need to re-run the put on the - // new table. Otherwise we lost the CAS to another racing put. - // Simply retry from the start. - if( V instanceof Prime ) - return putIfMatch(topmap,chm.copy_slot_and_check(topmap,kvs,idx,expVal),key,putval,expVal); - } - } - - // --- help_copy --------------------------------------------------------- - // Help along an existing resize operation. This is just a fast cut-out - // wrapper, to encourage inlining for the fast no-copy-in-progress case. We - // always help the top-most table copy, even if there are nested table - // copies in progress. - private final Object[] help_copy( Object[] helper ) { - // Read the top-level KVS only once. We'll try to help this copy along, - // even if it gets promoted out from under us (i.e., the copy completes - // and another KVS becomes the top-level copy). - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) return helper; // No copy in-progress - topchm.help_copy_impl(this,topkvs,false); - return helper; - } - - - // --- CHM ----------------------------------------------------------------- - // The control structure for the NonBlockingIdentityHashMap - private static final class CHM { - // Size in active K,V pairs - private final Counter _size; - public int size () { return (int)_size.get(); } - - // --- - // These next 2 fields are used in the resizing heuristics, to judge when - // it is time to resize or copy the table. Slots is a count of used-up - // key slots, and when it nears a large fraction of the table we probably - // end up reprobing too much. Last-resize-milli is the time since the - // last resize; if we are running back-to-back resizes without growing - // (because there are only a few live keys but many slots full of dead - // keys) then we need a larger table to cut down on the churn. - - // Count of used slots, to tell when table is full of dead unusable slots - private final Counter _slots; - public int slots() { return (int)_slots.get(); } - - // --- - // New mappings, used during resizing. - // The 'new KVs' array - created during a resize operation. This - // represents the new table being copied from the old one. It's the - // volatile variable that is read as we cross from one table to the next, - // to get the required memory orderings. It monotonically transits from - // null to set (once). - volatile Object[] _newkvs; - private final AtomicReferenceFieldUpdater _newkvsUpdater = - AtomicReferenceFieldUpdater.newUpdater(CHM.class,Object[].class, "_newkvs"); - // Set the _next field if we can. - boolean CAS_newkvs( Object[] newkvs ) { - while( _newkvs == null ) - if( _newkvsUpdater.compareAndSet(this,null,newkvs) ) - return true; - return false; - } - // Sometimes many threads race to create a new very large table. Only 1 - // wins the race, but the losers all allocate a junk large table with - // hefty allocation costs. Attempt to control the overkill here by - // throttling attempts to create a new table. I cannot really block here - // (lest I lose the non-blocking property) but late-arriving threads can - // give the initial resizing thread a little time to allocate the initial - // new table. The Right Long Term Fix here is to use array-lets and - // incrementally create the new very large array. In C I'd make the array - // with malloc (which would mmap under the hood) which would only eat - // virtual-address and not real memory - and after Somebody wins then we - // could in parallel initialize the array. Java does not allow - // un-initialized array creation (especially of ref arrays!). - volatile long _resizers; // count of threads attempting an initial resize - private static final AtomicLongFieldUpdater _resizerUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_resizers"); - - // --- - // Simple constructor - CHM( Counter size ) { - _size = size; - _slots= new Counter(); - } - - // --- tableFull --------------------------------------------------------- - // Heuristic to decide if this table is too full, and we should start a - // new table. Note that if a 'get' call has reprobed too many times and - // decided the table must be full, then always the estimate_sum must be - // high and we must report the table is full. If we do not, then we might - // end up deciding that the table is not full and inserting into the - // current table, while a 'get' has decided the same key cannot be in this - // table because of too many reprobes. The invariant is: - // slots.estimate_sum >= max_reprobe_cnt >= reprobe_limit(len) - private final boolean tableFull( int reprobe_cnt, int len ) { - return - // Do the cheap check first: we allow some number of reprobes always - reprobe_cnt >= REPROBE_LIMIT && - // More expensive check: see if the table is > 1/4 full. - _slots.estimate_get() >= reprobe_limit(len); - } - - // --- resize ------------------------------------------------------------ - // Resizing after too many probes. "How Big???" heuristics are here. - // Callers will (not this routine) will 'help_copy' any in-progress copy. - // Since this routine has a fast cutout for copy-already-started, callers - // MUST 'help_copy' lest we have a path which forever runs through - // 'resize' only to discover a copy-in-progress which never progresses. - private final Object[] resize( NonBlockingIdentityHashMap topmap, Object[] kvs) { - assert chm(kvs) == this; - - // Check for resize already in progress, probably triggered by another thread - Object[] newkvs = _newkvs; // VOLATILE READ - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // No copy in-progress, so start one. First up: compute new table size. - int oldlen = len(kvs); // Old count of K,V pairs allowed - int sz = size(); // Get current table count of active K,V pairs - int newsz = sz; // First size estimate - - // Heuristic to determine new size. We expect plenty of dead-slots-with-keys - // and we need some decent padding to avoid endless reprobing. - if( sz >= (oldlen>>2) ) { // If we are >25% full of keys then... - newsz = oldlen<<1; // Double size - if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - newsz = oldlen<<2; // Double double size - } - // This heuristic in the next 2 lines leads to a much denser table - // with a higher reprobe rate - //if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... - // newsz = oldlen<<1; // Double size - - // Last (re)size operation was very recent? Then double again; slows - // down resize operations for tables subject to a high key churn rate. - long tm = System.currentTimeMillis(); - long q=0; - if( newsz <= oldlen && // New table would shrink or hold steady? - tm <= topmap._last_resize_milli+10000 && // Recent resize (less than 1 sec ago) - (q=_slots.estimate_get()) >= (sz<<1) ) // 1/2 of keys are dead? - newsz = oldlen<<1; // Double the existing size - - // Do not shrink, ever - if( newsz < oldlen ) newsz = oldlen; - - // Convert to power-of-2 - int log2; - for( log2=MIN_SIZE_LOG; (1<>20/*megs*/; - if( r >= 2 && megs > 0 ) { // Already 2 guys trying; wait and see - newkvs = _newkvs; // Between dorking around, another thread did it - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - // TODO - use a wait with timeout, so we'll wakeup as soon as the new table - // is ready, or after the timeout in any case. - //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup - // For now, sleep a tad and see if the 2 guys already trying to make - // the table actually get around to making it happen. - try { Thread.sleep(8*megs); } catch( Exception e ) { } - } - // Last check, since the 'new' below is expensive and there is a chance - // that another thread slipped in a new thread while we ran the heuristic. - newkvs = _newkvs; - if( newkvs != null ) // See if resize is already in progress - return newkvs; // Use the new table already - - // Double size for K,V pairs, add 1 for CHM - newkvs = new Object[((1< _copyIdxUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyIdx"); - - // Work-done reporting. Used to efficiently signal when we can move to - // the new table. From 0 to len(oldkvs) refers to copying from the old - // table to the new. - volatile long _copyDone= 0; - static private final AtomicLongFieldUpdater _copyDoneUpdater = - AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyDone"); - - // --- help_copy_impl ---------------------------------------------------- - // Help along an existing resize operation. We hope its the top-level - // copy (it was when we started) but this CHM might have been promoted out - // of the top position. - private final void help_copy_impl( NonBlockingIdentityHashMap topmap, Object[] oldkvs, boolean copy_all ) { - assert chm(oldkvs) == this; - Object[] newkvs = _newkvs; - assert newkvs != null; // Already checked by caller - int oldlen = len(oldkvs); // Total amount to copy - final int MIN_COPY_WORK = Math.min(oldlen,1024); // Limit per-thread work - - // --- - int panic_start = -1; - int copyidx=-9999; // Fool javac to think it's initialized - while( _copyDone < oldlen ) { // Still needing to copy? - // Carve out a chunk of work. The counter wraps around so every - // thread eventually tries to copy every slot repeatedly. - - // We "panic" if we have tried TWICE to copy every slot - and it still - // has not happened. i.e., twice some thread somewhere claimed they - // would copy 'slot X' (by bumping _copyIdx) but they never claimed to - // have finished (by bumping _copyDone). Our choices become limited: - // we can wait for the work-claimers to finish (and become a blocking - // algorithm) or do the copy work ourselves. Tiny tables with huge - // thread counts trying to copy the table often 'panic'. - if( panic_start == -1 ) { // No panic? - copyidx = (int)_copyIdx; - while( copyidx < (oldlen<<1) && // 'panic' check - !_copyIdxUpdater.compareAndSet(this,copyidx,copyidx+MIN_COPY_WORK) ) - copyidx = (int)_copyIdx; // Re-read - if( !(copyidx < (oldlen<<1)) ) // Panic! - panic_start = copyidx; // Record where we started to panic-copy - } - - // We now know what to copy. Try to copy. - int workdone = 0; - for( int i=0; i 0 ) // Report work-done occasionally - copy_check_and_promote( topmap, oldkvs, workdone );// See if we can promote - //for( int i=0; i 0 ) { - while( !_copyDoneUpdater.compareAndSet(this,copyDone,copyDone+workdone) ) { - copyDone = _copyDone; // Reset, retry - assert (copyDone+workdone) <= oldlen; - } - //if( (10*copyDone/oldlen) != (10*(copyDone+workdone)/oldlen) ) - //System.out.print(" "+(copyDone+workdone)*100/oldlen+"%"+"_"+(_copyIdx*100/oldlen)+"%"); - } - - // Check for copy being ALL done, and promote. Note that we might have - // nested in-progress copies and manage to finish a nested copy before - // finishing the top-level copy. We only promote top-level copies. - if( copyDone+workdone == oldlen && // Ready to promote this table? - topmap._kvs == oldkvs && // Looking at the top-level table? - // Attempt to promote - topmap.CAS_kvs(oldkvs,_newkvs) ) { - topmap._last_resize_milli = System.currentTimeMillis(); // Record resize time for next check - //long nano = System.nanoTime(); - //System.out.println(" "+nano+" Promote table to "+len(_newkvs)); - //if( System.out != null ) System.out.print("]"); - } - } - - // --- copy_slot --------------------------------------------------------- - // Copy one K/V pair from oldkvs[i] to newkvs. Returns true if we can - // confirm that the new table guaranteed has a value for this old-table - // slot. We need an accurate confirmed-copy count so that we know when we - // can promote (if we promote the new table too soon, other threads may - // 'miss' on values not-yet-copied from the old table). We don't allow - // any direct updates on the new table, unless they first happened to the - // old table - so that any transition in the new table from null to - // not-null must have been from a copy_slot (or other old-table overwrite) - // and not from a thread directly writing in the new table. Thus we can - // count null-to-not-null transitions in the new table. - private boolean copy_slot( NonBlockingIdentityHashMap topmap, int idx, Object[] oldkvs, Object[] newkvs ) { - // Blindly set the key slot from null to TOMBSTONE, to eagerly stop - // fresh put's from inserting new values in the old table when the old - // table is mid-resize. We don't need to act on the results here, - // because our correctness stems from box'ing the Value field. Slamming - // the Key field is a minor speed optimization. - Object key; - while( (key=key(oldkvs,idx)) == null ) - CAS_key(oldkvs,idx, null, TOMBSTONE); - - // --- - // Prevent new values from appearing in the old table. - // Box what we see in the old table, to prevent further updates. - Object oldval = val(oldkvs,idx); // Read OLD table - while( !(oldval instanceof Prime) ) { - final Prime box = (oldval == null || oldval == TOMBSTONE) ? TOMBPRIME : new Prime(oldval); - if( CAS_val(oldkvs,idx,oldval,box) ) { // CAS down a box'd version of oldval - // If we made the Value slot hold a TOMBPRIME, then we both - // prevented further updates here but also the (absent) - // oldval is vaccuously available in the new table. We - // return with true here: any thread looking for a value for - // this key can correctly go straight to the new table and - // skip looking in the old table. - if( box == TOMBPRIME ) - return true; - // Otherwise we boxed something, but it still needs to be - // copied into the new table. - oldval = box; // Record updated oldval - break; // Break loop; oldval is now boxed by us - } - oldval = val(oldkvs,idx); // Else try, try again - } - if( oldval == TOMBPRIME ) return false; // Copy already complete here! - - // --- - // Copy the value into the new table, but only if we overwrite a null. - // If another value is already in the new table, then somebody else - // wrote something there and that write is happens-after any value that - // appears in the old table. If putIfMatch does not find a null in the - // new table - somebody else should have recorded the null-not_null - // transition in this copy. - Object old_unboxed = ((Prime)oldval)._V; - assert old_unboxed != TOMBSTONE; - boolean copied_into_new = (putIfMatch(topmap, newkvs, key, old_unboxed, null) == null); - - // --- - // Finally, now that any old value is exposed in the new table, we can - // forever hide the old-table value by slapping a TOMBPRIME down. This - // will stop other threads from uselessly attempting to copy this slot - // (i.e., it's a speed optimization not a correctness issue). - while( !CAS_val(oldkvs,idx,oldval,TOMBPRIME) ) - oldval = val(oldkvs,idx); - - return copied_into_new; - } // end copy_slot - } // End of CHM - - - // --- Snapshot ------------------------------------------------------------ - // The main class for iterating over the NBHM. It "snapshots" a clean - // view of the K/V array. - private class SnapshotV implements Iterator, Enumeration { - final Object[] _sskvs; - public SnapshotV() { - while( true ) { // Verify no table-copy-in-progress - Object[] topkvs = _kvs; - CHM topchm = chm(topkvs); - if( topchm._newkvs == null ) { // No table-copy-in-progress - // The "linearization point" for the iteration. Every key in this - // table will be visited, but keys added later might be skipped or - // even be added to a following table (also not iterated over). - _sskvs = topkvs; - break; - } - // Table copy in-progress - so we cannot get a clean iteration. We - // must help finish the table copy before we can start iterating. - topchm.help_copy_impl(NonBlockingIdentityHashMap.this,topkvs,true); - } - // Warm-up the iterator - next(); - } - int length() { return len(_sskvs); } - Object key(int idx) { return NonBlockingIdentityHashMap.key(_sskvs,idx); } - private int _idx; // Varies from 0-keys.length - private Object _nextK, _prevK; // Last 2 keys found - private TypeV _nextV, _prevV; // Last 2 values found - public boolean hasNext() { return _nextV != null; } - public TypeV next() { - // 'next' actually knows what the next value will be - it had to - // figure that out last go-around lest 'hasNext' report true and - // some other thread deleted the last value. Instead, 'next' - // spends all its effort finding the key that comes after the - // 'next' key. - if( _idx != 0 && _nextV == null ) throw new NoSuchElementException(); - _prevK = _nextK; // This will become the previous key - _prevV = _nextV; // This will become the previous value - _nextV = null; // We have no more next-key - // Attempt to set <_nextK,_nextV> to the next K,V pair. - // _nextV is the trigger: stop searching when it is != null - while( _idx elements() { return new SnapshotV(); } - - // --- values -------------------------------------------------------------- - /** Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are reflected - * in the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - @Override - public Collection values() { - return new AbstractCollection() { - @Override public void clear ( ) { NonBlockingIdentityHashMap.this.clear ( ); } - @Override public int size ( ) { return NonBlockingIdentityHashMap.this.size ( ); } - @Override public boolean contains( Object v ) { return NonBlockingIdentityHashMap.this.containsValue(v); } - @Override public Iterator iterator() { return new SnapshotV(); } - }; - } - - // --- keySet -------------------------------------------------------------- - private class SnapshotK implements Iterator, Enumeration { - final SnapshotV _ss; - public SnapshotK() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public TypeK next() { _ss.next(); return (TypeK)_ss._prevK; } - public boolean hasNext() { return _ss.hasNext(); } - public TypeK nextElement() { return next(); } - public boolean hasMoreElements() { return hasNext(); } - } - - /** Returns an enumeration of the keys in this table. - * @return an enumeration of the keys in this table - * @see #keySet() */ - public Enumeration keys() { return new SnapshotK(); } - - /** Returns a {@link Set} view of the keys contained in this map. The set - * is backed by the map, so changes to the map are reflected in the set, - * and vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. */ - @Override - public Set keySet() { - return new AbstractSet () { - @Override public void clear ( ) { NonBlockingIdentityHashMap.this.clear ( ); } - @Override public int size ( ) { return NonBlockingIdentityHashMap.this.size ( ); } - @Override public boolean contains( Object k ) { return NonBlockingIdentityHashMap.this.containsKey(k); } - @Override public boolean remove ( Object k ) { return NonBlockingIdentityHashMap.this.remove (k) != null; } - @Override public Iterator iterator() { return new SnapshotK(); } - }; - } - - - // --- entrySet ------------------------------------------------------------ - // Warning: Each call to 'next' in this iterator constructs a new NBHMEntry. - private class NBHMEntry extends AbstractEntry { - NBHMEntry( final TypeK k, final TypeV v ) { super(k,v); } - public TypeV setValue(final TypeV val) { - if( val == null ) throw new NullPointerException(); - _val = val; - return put(_key, val); - } - } - - private class SnapshotE implements Iterator> { - final SnapshotV _ss; - public SnapshotE() { _ss = new SnapshotV(); } - public void remove() { _ss.remove(); } - public Entry next() { _ss.next(); return new NBHMEntry((TypeK)_ss._prevK,_ss._prevV); } - public boolean hasNext() { return _ss.hasNext(); } - } - - /** Returns a {@link Set} view of the mappings contained in this map. The - * set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes - * the corresponding mapping from the map, via the - * Iterator.remove, Set.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - * - *

Warning: the iterator associated with this Set - * requires the creation of {@link Entry} objects with each - * iteration. The {@link NonBlockingIdentityHashMap} does not normally create or - * using {@link Entry} objects so they will be created soley - * to support this iteration. Iterating using {@link #keySet} or {@link - * #values} will be more efficient. - */ - @Override - public Set> entrySet() { - return new AbstractSet>() { - @Override public void clear ( ) { NonBlockingIdentityHashMap.this.clear( ); } - @Override public int size ( ) { return NonBlockingIdentityHashMap.this.size ( ); } - @Override public boolean remove( final Object o ) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - return NonBlockingIdentityHashMap.this.remove(e.getKey(), e.getValue()); - } - @Override public boolean contains(final Object o) { - if( !(o instanceof Entry)) return false; - final Entry e = (Entry)o; - TypeV v = get(e.getKey()); - return v.equals(e.getValue()); - } - @Override public Iterator> iterator() { return new SnapshotE(); } - }; - } - - // --- writeObject ------------------------------------------------------- - // Write a NBHM to a stream - private void writeObject(java.io.ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); // Nothing to write - for( Object K : keySet() ) { - final Object V = get(K); // Do an official 'get' - s.writeObject(K); // Write the pair - s.writeObject(V); - } - s.writeObject(null); // Sentinel to indicate end-of-data - s.writeObject(null); - } - - // --- readObject -------------------------------------------------------- - // Read a CHM from a stream - private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { - s.defaultReadObject(); // Read nothing - initialize(MIN_SIZE); - for(;;) { - final TypeK K = (TypeK) s.readObject(); - final TypeV V = (TypeV) s.readObject(); - if( K == null ) break; - put(K,V); // Insert with an offical put - } - } - -} // End NonBlockingIdentityHashMap class diff --git a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingSetInt.java b/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingSetInt.java deleted file mode 100644 index 849fc643b..000000000 --- a/libtest/src/main/java/org/cliffc/high_scale_lib/old/NonBlockingSetInt.java +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Written by Cliff Click and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ - -package org.cliffc.high_scale_lib.old; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.atomic.*; - -import sun.misc.Unsafe; - -/** - * A multi-threaded bit-vector set, implemented as an array of primitive - * {@code longs}. All operations are non-blocking and multi-threaded safe. - * {@link #contains(int)} calls are roughly the same speed as a {load, mask} - * sequence. {@link #add(int)} and {@link #remove(int)} calls are a tad more - * expensive than a {load, mask, store} sequence because they must use a CAS. - * The bit-vector is auto-sizing. - * - *

General note of caution: The Set API allows the use of {@link Integer} - * with silent autoboxing - which can be very expensive if many calls are - * being made. Since autoboxing is silent you may not be aware that this is - * going on. The built-in API takes lower-case {@code ints} and is much more - * efficient. - * - *

Space: space is used in proportion to the largest element, as opposed to - * the number of elements (as is the case with hash-table based Set - * implementations). Space is approximately (largest_element/8 + 64) bytes. - * - * The implementation is a simple bit-vector using CAS for update. - * - * @since 1.5 - * @author Cliff Click - */ - -public class NonBlockingSetInt extends AbstractSet implements Serializable { - private static final long serialVersionUID = 1234123412341234123L; - private static final Unsafe _unsafe = UtilUnsafe.getUnsafe(); - - // --- Bits to allow atomic update of the NBSI - private static final long _nbsi_offset; - static { // - Field f = null; - try { - f = NonBlockingSetInt.class.getDeclaredField("_nbsi"); - } catch( NoSuchFieldException e ) { - } - _nbsi_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS_nbsi( NBSI old, NBSI nnn ) { - return _unsafe.compareAndSwapObject(this, _nbsi_offset, old, nnn ); - } - - // The actual Set of Joy, which changes during a resize event. The - // Only Field for this class, so I can atomically change the entire - // set implementation with a single CAS. - private transient NBSI _nbsi; - - /** Create a new empty bit-vector */ - public NonBlockingSetInt( ) { - _nbsi = new NBSI(63, new Counter(), this); // The initial 1-word set - } - - private NonBlockingSetInt(NonBlockingSetInt a, NonBlockingSetInt b) { - _nbsi = new NBSI(a._nbsi,b._nbsi,new Counter(),this); - } - - /** - * Add {@code i} to the set. Uppercase {@link Integer} version of add, - * requires auto-unboxing. When possible use the {@code int} version of - * {@link #add(int)} for efficiency. - * @throws IllegalArgumentException if i is negative. - * @return true if i was added to the set. - */ - public boolean add ( final Integer i ) { - return add(i.intValue()); - } - /** - * Test if {@code o} is in the set. This is the uppercase {@link Integer} - * version of contains, requires a type-check and auto-unboxing. When - * possible use the {@code int} version of {@link #contains(int)} for - * efficiency. - * @return true if i was in the set. - */ - public boolean contains( final Object o ) { - return o instanceof Integer ? contains(((Integer)o).intValue()) : false; - } - /** - * Remove {@code o} from the set. This is the uppercase {@link Integer} - * version of remove, requires a type-check and auto-unboxing. When - * possible use the {@code int} version of {@link #remove(int)} for - * efficiency. - * @return true if i was removed to the set. - */ - public boolean remove( final Object o ) { - return o instanceof Integer ? remove (((Integer)o).intValue()) : false; - } - - /** - * Add {@code i} to the set. This is the lower-case '{@code int}' version - * of {@link #add} - no autoboxing. Negative values throw - * IllegalArgumentException. - * @throws IllegalArgumentException if i is negative. - * @return true if i was added to the set. - */ - public boolean add( final int i ) { - if( i < 0 ) throw new IllegalArgumentException(""+i); - return _nbsi.add(i); - } - /** - * Test if {@code i} is in the set. This is the lower-case '{@code int}' - * version of {@link #contains} - no autoboxing. - * @return true if i was int the set. - */ - public boolean contains( final int i ) { return i<0 ? false : _nbsi.contains(i); } - /** - * Remove {@code i} from the set. This is the fast lower-case '{@code int}' - * version of {@link #remove} - no autoboxing. - * @return true if i was added to the set. - */ - public boolean remove ( final int i ) { return i<0 ? false : _nbsi.remove (i); } - - /** - * Current count of elements in the set. Due to concurrent racing updates, - * the size is only ever approximate. Updates due to the calling thread are - * immediately visible to calling thread. - * @return count of elements. - */ - public int size ( ) { return _nbsi.size( ); } - /** Empty the bitvector. */ - public void clear ( ) { - NBSI cleared = new NBSI(63, new Counter(), this); // An empty initial NBSI - while( !CAS_nbsi( _nbsi, cleared ) ) // Spin until clear works - ; - } - - public int sizeInBytes() { return _nbsi.sizeInBytes(); } - - /***************************************************************** - * - * bitwise comparisons optimised for NBSI - * - *****************************************************************/ - - public NonBlockingSetInt intersect(final NonBlockingSetInt op) { - NonBlockingSetInt res = new NonBlockingSetInt(this,op); - res._nbsi.intersect(res._nbsi, this._nbsi, op._nbsi); - return res; - } - - public NonBlockingSetInt union(final NonBlockingSetInt op) { - NonBlockingSetInt res = new NonBlockingSetInt(this,op); - res._nbsi.union(res._nbsi, this._nbsi, op._nbsi); - return res; - } - -// public NonBlockingSetInt not(final NonBlockingSetInt op) { -// -// } - - /** Verbose printout of internal structure for debugging. */ - public void print() { _nbsi.print(0); } - - /** - * Standard Java {@link Iterator}. Not very efficient because it - * auto-boxes the returned values. - */ - public Iterator iterator( ) { return new iter(); } - - public IntIterator intIterator() { return new NBSIIntIterator(); } - - private class NBSIIntIterator implements IntIterator { - - NBSI nbsi; - int index = -1; - int prev = -1; - - NBSIIntIterator() { - nbsi = _nbsi; - advance(); - } - - private void advance() { - while( true ) { - index++; // Next index - while( (index>>6) >= nbsi._bits.length ) { // Index out of range? - if( nbsi._new == null ) { // New table? - index = -2; // No, so must be all done - return; // - } - nbsi = nbsi._new; // Carry on, in the new table - } - if( nbsi.contains(index) ) return; - } - } - @Override - public int next() { - if( index == -1 ) throw new NoSuchElementException(); - prev = index; - advance(); - return prev; - } - - @Override - public boolean hasNext() { - return index != -2; - } - - public void remove() { - if( prev == -1 ) throw new IllegalStateException(); - nbsi.remove(prev); - prev = -1; - } - } - - private class iter implements Iterator { - NBSIIntIterator intIterator; - iter() { intIterator = new NBSIIntIterator(); } - public boolean hasNext() { return intIterator.hasNext(); } - public Integer next() { return intIterator.next(); } - public void remove() { intIterator.remove(); } - } - - // --- writeObject ------------------------------------------------------- - // Write a NBSI to a stream - private void writeObject(java.io.ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); // Nothing to write - final NBSI nbsi = _nbsi; // The One Field is transient - final int len = _nbsi._bits.length<<6; - s.writeInt(len); // Write max element - for( int i=0; i= 0 && idx < ary.length; - return _Lbase + idx * _Lscale; - } - private final boolean CAS( int idx, long old, long nnn ) { - return _unsafe.compareAndSwapLong( _bits, rawIndex(_bits, idx), old, nnn ); - } - - // --- Resize - // The New Table, only set once to non-zero during a resize. - // Must be atomically set. - private NBSI _new; - private static final long _new_offset; - static { // - Field f = null; - try { - f = NBSI.class.getDeclaredField("_new"); - } catch( NoSuchFieldException e ) { - } - _new_offset = _unsafe.objectFieldOffset(f); - } - private final boolean CAS_new( NBSI nnn ) { - return _unsafe.compareAndSwapObject(this, _new_offset, null, nnn ); - } - - private transient final AtomicInteger _copyIdx; // Used to count bits started copying - private transient final AtomicInteger _copyDone; // Used to count words copied in a resize operation - private transient final int _sum_bits_length; // Sum of all nested _bits.lengths - - private static final long mask( int i ) { return 1L<<(i&63); } - - // I need 1 free bit out of 64 to allow for resize. I do this by stealing - // the high order bit - but then I need to do something with adding element - // number 63 (and friends). I could use a mod63 function but it's more - // efficient to handle the mod-64 case as an exception. - // - // Every 64th bit is put in it's own recursive bitvector. If the low 6 bits - // are all set, we shift them off and recursively operate on the _nbsi64 set. - private final NBSI _nbsi64; - - private NBSI( int max_elem, Counter ctr, NonBlockingSetInt nonb ) { - super(); - _non_blocking_set_int = nonb; - _size = ctr; - _copyIdx = ctr == null ? null : new AtomicInteger(); - _copyDone = ctr == null ? null : new AtomicInteger(); - // The main array of bits - _bits = new long[(int)(((long)max_elem+63)>>>6)]; - // Every 64th bit is moved off to it's own subarray, so that the - // sign-bit is free for other purposes - _nbsi64 = ((max_elem+1)>>>6) == 0 ? null : new NBSI((max_elem+1)>>>6, null, null); - _sum_bits_length = _bits.length + (_nbsi64==null ? 0 : _nbsi64._sum_bits_length); - } - - /** built a new NBSI with buffers large enough to hold bitwise operations on the operands **/ - private NBSI(NBSI a, NBSI b, Counter ctr, NonBlockingSetInt nonb) { - super(); - _non_blocking_set_int = nonb; - _size = ctr; - _copyIdx = ctr == null ? null : new AtomicInteger(); - _copyDone = ctr == null ? null : new AtomicInteger(); - - if(!has_bits(a) && !has_bits(b)) { - _bits = null; - _nbsi64 = null; - _sum_bits_length = 0; - return; - } - - // todo - clean this nastiness up - // essentially just safely creates new empty buffers for each of the recursive bitsets - if(!has_bits(a)) { - _bits = new long[b._bits.length]; - _nbsi64 = new NBSI(null,b._nbsi64,null,null); - } else if(!has_bits(b)) { - _bits = new long[a._bits.length]; - _nbsi64 = new NBSI(null,a._nbsi64,null,null); - } else { - int bit_length = a._bits.length > b._bits.length ? a._bits.length : b._bits.length; - _bits = new long[bit_length]; - _nbsi64 = new NBSI(a._nbsi64,b._nbsi64,null,null); - } - _sum_bits_length = _bits.length + _nbsi64._sum_bits_length; - } - - private static boolean has_bits(NBSI n) { - return n != null && n._bits != null; - } - - // Lower-case 'int' versions - no autoboxing, very fast. - // 'i' is known positive. - public boolean add( final int i ) { - // Check for out-of-range for the current size bit vector. - // If so we need to grow the bit vector. - if( (i>>6) >= _bits.length ) - return install_larger_new_bits(i). // Install larger pile-o-bits (duh) - help_copy().add(i); // Finally, add to the new table - - // Handle every 64th bit via using a nested array - NBSI nbsi = this; // The bit array being added into - int j = i; // The bit index being added - while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) - nbsi = nbsi._nbsi64; // Recurse - j = j>>6; // Strip off low 6 bits (all set) - } - - final long mask = mask(j); - long old; - do { - old = nbsi._bits[j>>6]; // Read old bits - if( old < 0 ) // Not mutable? - // Not mutable: finish copy of word, and retry on copied word - return help_copy_impl(i).help_copy().add(i); - if( (old & mask) != 0 ) return false; // Bit is already set? - } while( !nbsi.CAS( j>>6, old, old | mask ) ); - _size.add(1); - return true; - } - - public boolean remove( final int i ) { - if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array! - return _new==null ? false : help_copy().remove(i); - - // Handle every 64th bit via using a nested array - NBSI nbsi = this; // The bit array being added into - int j = i; // The bit index being added - while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) - nbsi = nbsi._nbsi64; // Recurse - j = j>>6; // Strip off low 6 bits (all set) - } - - final long mask = mask(j); - long old; - do { - old = nbsi._bits[j>>6]; // Read old bits - if( old < 0 ) // Not mutable? - // Not mutable: finish copy of word, and retry on copied word - return help_copy_impl(i).help_copy().remove(i); - if( (old & mask) == 0 ) return false; // Bit is already clear? - } while( !nbsi.CAS( j>>6, old, old & ~mask ) ); - _size.add(-1); - return true; - } - - public boolean contains( final int i ) { - if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array! - return _new==null ? false : help_copy().contains(i); - - // Handle every 64th bit via using a nested array - NBSI nbsi = this; // The bit array being added into - int j = i; // The bit index being added - while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) - nbsi = nbsi._nbsi64; // Recurse - j = j>>6; // Strip off low 6 bits (all set) - } - - final long mask = mask(j); - long old = nbsi._bits[j>>6]; // Read old bits - if( old < 0 ) // Not mutable? - // Not mutable: finish copy of word, and retry on copied word - return help_copy_impl(i).help_copy().contains(i); - // Yes mutable: test & return bit - return (old & mask) != 0; - } - - /** - * Bitwise operations which store the result in this instance. - * Assumes that this instance contains ample buffer space to store the largest - * buffer from each NBSI in the recursive bitmap. - * - * Also assumes that this method is called during the construction process of - * the bitset before the instance could be leaked to multiple threads. - ***/ - public boolean intersect(NBSI dest, NBSI a, NBSI b) { - // terminate recursion if one bitset is missing data - // since that word should be left as 0L anyway - if(!has_bits(a) || !has_bits(b)) - return true; - for(int i = 0; i < dest._bits.length; i++) { - long left = a.safe_read_word(i,0L); - long right = b.safe_read_word(i,0L); - dest._bits[i] = (left & right) & Long.MAX_VALUE; // mask sign bit - } - // todo - recompute size - return intersect(dest._nbsi64, a._nbsi64, b._nbsi64); - } - - public boolean union(NBSI dest, NBSI a, NBSI b) { - // terminate recursion if neiter bitset has data - if(!has_bits(a) && !has_bits(b)) - return true; - if(has_bits(a) || has_bits(b)) { - for(int i = 0; i < dest._bits.length; i++) { - long left = a.safe_read_word(i,0); - long right = b.safe_read_word(i,0); - dest._bits[i] = (left | right) & Long.MAX_VALUE; - } - } - return union(dest._nbsi64, a._nbsi64, b._nbsi64); - } - - /**************************************************************************/ - - private long safe_read_word(int i, long default_word) { - if(i >= _bits.length) { - // allow reading past the end of the buffer filling in a default word - return default_word; - } - long word = _bits[i]; - if(word < 0) { - word = help_copy_impl(i).help_copy()._bits[i]; - } - return word; - } - - public int sizeInBytes() { return (int)_bits.length; } - - public int size() { return (int)_size.get(); } - - // Must grow the current array to hold an element of size i - private NBSI install_larger_new_bits( final int i ) { - if( _new == null ) { - // Grow by powers of 2, to avoid minor grow-by-1's. - // Note: must grow by exact powers-of-2 or the by-64-bit trick doesn't work right - int sz = (_bits.length<<6)<<1; - // CAS to install a new larger size. Did it work? Did it fail? We - // don't know and don't care. Only One can be installed, so if - // another thread installed a too-small size, we can't help it - we - // must simply install our new larger size as a nested-resize table. - CAS_new(new NBSI(sz, _size, _non_blocking_set_int)); - } - // Return self for 'fluid' programming style - return this; - } - - // Help any top-level NBSI to copy until completed. - // Always return the _new version of *this* NBSI, in case we're nested. - private NBSI help_copy() { - // Pick some words to help with - but only help copy the top-level NBSI. - // Nested NBSI waits until the top is done before we start helping. - NBSI top_nbsi = _non_blocking_set_int._nbsi; - final int HELP = 8; // Tuning number: how much copy pain are we willing to inflict? - // We "help" by forcing individual bit indices to copy. However, bits - // come in lumps of 64 per word, so we just advance the bit counter by 64's. - int idx = top_nbsi._copyIdx.getAndAdd(64*HELP); - for( int i=0; i>6; // Strip off low 6 bits (all set) - } - - // Transit from state 1: word is not immutable yet - // Immutable is in bit 63, the sign bit. - long bits = old._bits[j>>6]; - while( bits >= 0 ) { // Still in state (1)? - long oldbits = bits; - bits |= mask(63); // Target state of bits: sign-bit means immutable - if( old.CAS( j>>6, oldbits, bits ) ) { - if( oldbits == 0 ) _copyDone.addAndGet(1); - break; // Success - old array word is now immutable - } - bits = old._bits[j>>6]; // Retry if CAS failed - } - - // Transit from state 2: non-zero in old and zero in new - if( bits != mask(63) ) { // Non-zero in old? - long new_bits = nnn._bits[j>>6]; - if( new_bits == 0 ) { // New array is still zero - new_bits = bits & ~mask(63); // Desired new value: a mutable copy of bits - // One-shot CAS attempt, no loop, from 0 to non-zero. - // If it fails, somebody else did the copy for us - if( !nnn.CAS( j>>6, 0, new_bits ) ) - new_bits = nnn._bits[j>>6]; // Since it failed, get the new value - assert new_bits != 0; - } - - // Transit from state 3: non-zero in old and non-zero in new - // One-shot CAS attempt, no loop, from non-zero to 0 (but immutable) - if( old.CAS( j>>6, bits, mask(63) ) ) - _copyDone.addAndGet(1); // One more word finished copying - } - - // Now in state 4: zero (and immutable) in old - - // Return the self bitvector for 'fluid' programming style - return this; - } - - private void print( int d, String msg ) { - for( int i=0; i. - * #L% - */ -import java.lang.reflect.Field; -import sun.misc.Unsafe; - -/** - * Simple class to obtain access to the {@link Unsafe} object. {@link Unsafe} - * is required to allow efficient CAS operations on arrays. Note that the - * versions in {@link java.util.concurrent.atomic}, such as {@link - * java.util.concurrent.atomic.AtomicLongArray}, require extra memory ordering - * guarantees which are generally not needed in these algorithms and are also - * expensive on most processors. - */ -class UtilUnsafe { - private UtilUnsafe() { } // dummy private constructor - /** Fetch the Unsafe. Use With Caution. */ - public static Unsafe getUnsafe() { - // Not on bootclasspath - if( UtilUnsafe.class.getClassLoader() == null ) - return Unsafe.getUnsafe(); - try { - final Field fld = Unsafe.class.getDeclaredField("theUnsafe"); - fld.setAccessible(true); - return (Unsafe) fld.get(UtilUnsafe.class); - } catch (Exception e) { - throw new RuntimeException("Could not obtain access to sun.misc.Unsafe", e); - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputBusy.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputBusy.java deleted file mode 100644 index 1951e53ae..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputBusy.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.benchmarks.handrolled; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; - -import psy.lob.saw.queues.common.SPSCQueueFactory; - -public class QueueThroughputBusy { - // 15 == 32 * 1024 - public static final int QUEUE_CAPACITY = 1 << Integer.getInteger("scale", 17); - public static final int REPETITIONS = Integer.getInteger("reps", 50) * 1000 * 1000; - public static final Integer TEST_VALUE = Integer.valueOf(777); - - public static void main(final String[] args) throws Exception { - System.out.println("capacity:" + QUEUE_CAPACITY + " reps:" + REPETITIONS); - final Queue queue = SPSCQueueFactory.createQueue(Integer.parseInt(args[0]), Integer.getInteger("scale", 17)); - - final long[] results = new long[20]; - for (int i = 0; i < 20; i++) { - System.gc(); - results[i] = performanceRun(i, queue); - } - // only average last 10 results for summary - long sum = 0; - for (int i = 10; i < 20; i++) { - sum += results[i]; - } - System.out.format("summary,QueuePerfTest3,%s,%d\n", queue.getClass().getSimpleName(), sum / 10); - } - - private static long performanceRun(int runNumber, Queue queue) throws Exception { - Producer p = new Producer(queue); - Thread thread = new Thread(p); - thread.start();// producer will timestamp start - - Integer result; - int i = REPETITIONS; - int queueEmpty = 0; - do { - while (null == (result = queue.poll())) { - queueEmpty++; - } - } while (0 != --i); - long end = System.nanoTime(); - - thread.join(); - long duration = end - p.start; - long ops = (REPETITIONS * 1000L * 1000L * 1000L) / duration; - String qName = queue.getClass().getSimpleName(); - System.out.format("%d - ops/sec=%,d - %s result=%d failed.poll=%d failed.offer=%d\n", runNumber, ops, - qName, result, queueEmpty, p.queueFull); - return ops; - } - - public static class Producer implements Runnable { - private final Queue queue; - int queueFull = 0; - volatile long start = 0; - - public Producer(Queue queue) { - this.queue = queue; - } - - public void run() { - int i = REPETITIONS; - int f = 0; - Queue q = queue; - long s = System.nanoTime(); - do { - while (!q.offer(TEST_VALUE)) { - f++; - } - } while (0 != --i); - - queueFull = f; - start = s; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputYield.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputYield.java deleted file mode 100644 index 0c9ef684a..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/handrolled/QueueThroughputYield.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.benchmarks.handrolled; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; - -import psy.lob.saw.queues.common.SPSCQueueFactory; - -public class QueueThroughputYield { - // 15 == 32 * 1024 - public static final int QUEUE_CAPACITY = 1 << Integer.getInteger("scale", 17); - public static final int REPETITIONS = Integer.getInteger("reps", 50) * 1000 * 1000; - public static final Integer TEST_VALUE = Integer.valueOf(777); - - public static void main(final String[] args) throws Exception { - System.out.println("capacity:" + QUEUE_CAPACITY + " reps:" + REPETITIONS); - final Queue queue = SPSCQueueFactory.createQueue(Integer.parseInt(args[0]), Integer.getInteger("scale", 17)); - - final long[] results = new long[20]; - for (int i = 0; i < 20; i++) { - System.gc(); - results[i] = performanceRun(i, queue); - } - // only average last 10 results for summary - long sum = 0; - for (int i = 10; i < 20; i++) { - sum += results[i]; - } - System.out.format("summary,QueuePerfTest2,%s,%d\n", queue.getClass().getSimpleName(), sum / 10); - } - - private static long performanceRun(int runNumber, Queue queue) throws Exception { - Producer p = new Producer(queue); - Thread thread = new Thread(p); - thread.start();// producer will timestamp start - - Integer result; - int i = REPETITIONS; - int queueEmpty = 0; - do { - while (null == (result = queue.poll())) { - queueEmpty++; - Thread.yield(); - } - } while (0 != --i); - long end = System.nanoTime(); - - thread.join(); - long duration = end - p.start; - long ops = (REPETITIONS * 1000L * 1000L * 1000L) / duration; - String qName = queue.getClass().getSimpleName(); - System.out.format("%d - ops/sec=%,d - %s result=%d failed.poll=%d failed.offer=%d\n", runNumber, ops, - qName, result, queueEmpty, p.queueFull); - return ops; - } - - public static class Producer implements Runnable { - private final Queue queue; - int queueFull = 0; - volatile long start = 0; - - public Producer(Queue queue) { - this.queue = queue; - } - - public void run() { - int i = REPETITIONS; - int f = 0; - Queue q = queue; - long s = System.nanoTime(); - do { - while (!q.offer(TEST_VALUE)) { - Thread.yield(); - f++; - } - } while (0 != --i); - - queueFull = f; - start = s; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueBenchmark.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueBenchmark.java deleted file mode 100644 index 4541456f6..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueBenchmark.java +++ /dev/null @@ -1,47 +0,0 @@ -package psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; - -import psy.lob.saw.queues.common.SPSCQueueFactory; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; - -@State(Scope.Benchmark) -public abstract class QueueBenchmark { - @Param(value={"11","12","21","22","23","24","25","31","32","33","41","42"}) - protected int queueType; - @Param(value={"17"}) - protected int queueScale; - protected static Queue q; - - @Setup(Level.Trial) - public void createQueue() - { - q = SPSCQueueFactory.createQueue(queueType, queueScale); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueRoundTripLatency.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueRoundTripLatency.java deleted file mode 100644 index 386b6a5e0..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueRoundTripLatency.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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 psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; -import java.util.concurrent.TimeUnit; - -import psy.lob.saw.queues.common.SPSCQueueFactory; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Group; -import org.openjdk.jmh.annotations.GroupThreads; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.logic.Control; - -@State(Scope.Benchmark) -@BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -public class QueueRoundTripLatency { - private static final Integer DUMMY_MESSAGE = 1; - @Param(value={"11","12","21","22","23","24","25","31","32","33","41","42"}) - protected int queueType; - @Param(value={"17"}) - protected int queueScale; - - private final static int BURST_SIZE = Integer.getInteger("burst.size",1); - - private static volatile Queue ping; - protected static volatile Queue pong; - - @Setup(Level.Trial) - public void createPueue() - { - ping = SPSCQueueFactory.createQueue(queueType, queueScale); - pong = SPSCQueueFactory.createQueue(queueType, queueScale); - // This is an estimate, but for bounded queues if the burst size is more than actual ring capacity - // the benchmark will hang -// if (burstSize > (1 << queueScale)) { -// throw new IllegalArgumentException("Batch size exceeds estimated capacity"); -// } - } - - @State(Scope.Thread) - public static class Link { - final Queue in; - final Queue out; - - public Link() { - this.in = ping; - this.out = pong; - } - - public void link() { - Integer e = in.poll(); - if (e != null) { - out.offer(e); - } - } - - /** - * We want to always start with an empty inbound. Iteration tear downs are synchronized. - */ - @TearDown(Level.Iteration) - public void clear() { - // SPSC -> consumer must clear the queue - in.clear(); - ping = in; - pong = out; - } - } - - @State(Scope.Thread) - public static class Source { - final Queue start; - final Queue end; - public Source() { - this.end = pong; - this.start = ping; - } - - public void ping(Control ctl) { - for (int i = 0; i < BURST_SIZE; i++) { - start.offer(DUMMY_MESSAGE); - } - for (int i = 0; i < BURST_SIZE; i++) { - while (!ctl.stopMeasurement && end.poll() == null) { - } - } - } - - /** - * We want to always start with an empty inbound. Iteration tear downs are synchronized. - */ - @TearDown(Level.Iteration) - public void clear() { - // SPSC -> consumer must clear the queue - end.clear(); - ping = start; - pong = end; - } - } - - @GenerateMicroBenchmark - @Group("ring") - @GroupThreads(1) - public void ping(Control ctl, Source s) { - s.ping(ctl); - } - - @GenerateMicroBenchmark - @Group("ring") - @GroupThreads(1) - public void loop(Link l) { - l.link(); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputBusy.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputBusy.java deleted file mode 100644 index e3318970c..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputBusy.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.AuxCounters; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Group; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.logic.BlackHole; - -@State(Scope.Group) -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) -public class QueueThroughputBusy extends QueueBenchmark { - private static final long DELAY_PRODUCER = Long.getLong("delay.p", 0L); - private static final long DELAY_CONSUMER = Long.getLong("delay.c", 0L); - private static final Integer ONE = 777; - - @AuxCounters - @State(Scope.Thread) - public static class OpCounters { - public int pollFail, offerFail; - - @Setup(Level.Iteration) - public void clean() { - pollFail = offerFail = 0; - } - } - - private static ThreadLocal marker = new ThreadLocal<>(); - - @State(Scope.Thread) - public static class ConsumerMarker { - public ConsumerMarker() { - marker.set(this); - } - } - - @GenerateMicroBenchmark - @Group("tpt") - public void offer(OpCounters counters) { - if (!q.offer(ONE)) { - counters.offerFail++; - } - if (DELAY_PRODUCER != 0) { - BlackHole.consumeCPU(DELAY_PRODUCER); - } - } - - @GenerateMicroBenchmark - @Group("tpt") - public void poll(OpCounters counters, ConsumerMarker cm) { - if (q.poll() == null) { - counters.pollFail++; - } - if (DELAY_CONSUMER != 0) { - BlackHole.consumeCPU(DELAY_CONSUMER); - } - } - - @TearDown(Level.Iteration) - public void emptyQ() { - if (marker.get() == null) - return; - // sadly the iteration tear down is performed from each participating thread, so we need to guess - // which is which (can't have concurrent access to poll). - while (q.poll() != null) - ; - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputYield.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputYield.java deleted file mode 100644 index e380ec6c0..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/QueueThroughputYield.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.AuxCounters; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Group; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.logic.BlackHole; - -@State(Scope.Group) -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) -public class QueueThroughputYield extends QueueBenchmark { - private static final long DELAY_PRODUCER = Long.getLong("delay.p", 0L); - private static final long DELAY_CONSUMER = Long.getLong("delay.c", 0L); - private static final Integer ONE = 777; - - @AuxCounters - @State(Scope.Thread) - public static class OpCounters { - public int pollFail, offerFail; - - @Setup(Level.Iteration) - public void clean() { - pollFail = offerFail = 0; - } - } - - private static ThreadLocal marker = new ThreadLocal<>(); - - @State(Scope.Thread) - public static class ConsumerMarker { - public ConsumerMarker() { - marker.set(this); - } - } - - @GenerateMicroBenchmark - @Group("tpt") - public void offer(OpCounters counters) { - if (!q.offer(ONE)) { - counters.offerFail++; - Thread.yield(); - } - if (DELAY_PRODUCER != 0) { - BlackHole.consumeCPU(DELAY_PRODUCER); - } - } - - @GenerateMicroBenchmark - @Group("tpt") - public void poll(OpCounters counters, ConsumerMarker cm) { - if (q.poll() == null) { - counters.pollFail++; - Thread.yield(); - } - if (DELAY_CONSUMER != 0) { - BlackHole.consumeCPU(DELAY_CONSUMER); - } - } - - @TearDown(Level.Iteration) - public void emptyQ() { - if (marker.get() == null) - return; - // sadly the iteration tear down is performed from each participating thread, so we need to guess - // which is which (can't have concurrent access to poll). - while (q.poll() != null) - ; - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedOffer.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedOffer.java deleted file mode 100644 index 28de76675..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedOffer.java +++ /dev/null @@ -1,68 +0,0 @@ -package psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - -import java.util.Queue; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OperationsPerInvocation; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.NANOSECONDS) -@BenchmarkMode({Mode.AverageTime}) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class SingleThreadedOffer extends QueueBenchmark -{ - public static final int CAPACITY = 1 << 15; - public static final Integer TOKEN = 1; - - - @Setup(Level.Invocation) - public void clear() - { - q.clear(); - } - - @GenerateMicroBenchmark - @OperationsPerInvocation(CAPACITY) - public void offer() - { - final Queue lq = q; - for (int i = 0; i < CAPACITY; i++) - { - lq.offer(TOKEN); - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedPoll.java b/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedPoll.java deleted file mode 100644 index 274e9a114..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/benchmarks/jmh/SingleThreadedPoll.java +++ /dev/null @@ -1,69 +0,0 @@ -package psy.lob.saw.queues.benchmarks.jmh; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OperationsPerInvocation; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.NANOSECONDS) -@BenchmarkMode({Mode.AverageTime}) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class SingleThreadedPoll extends QueueBenchmark -{ - public static final int CAPACITY = 1 << 14; - public static final Integer TOKEN = 1; - - @Setup(Level.Invocation) - public void fill() - { - for( int i=0; i lq = q; - for (int i = 0; i < CAPACITY; i++) - { - lq.poll(); - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray1ReadWrite.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray1ReadWrite.java deleted file mode 100644 index 61568502d..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray1ReadWrite.java +++ /dev/null @@ -1,88 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@BenchmarkMode({ Mode.Throughput }) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class CircularArray1ReadWrite { - public static final int CAPACITY = 1 << 15; - public static final Integer TOKEN = 1; - - private CircularArrayQueue1 caq = new CircularArrayQueue1(CAPACITY) { - @Override - public boolean offer(Integer e) { - return false; - } - - @Override - public Integer poll() { - return null; - } - - @Override - public Integer peek() { - return null; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public int size() { - return 0; - } - }; - - long index; - - @GenerateMicroBenchmark - public void offer() { - int offset = caq.calcOffset(index++); - caq.spElement(offset, TOKEN); - } - - @GenerateMicroBenchmark - public void poll() { - int offset = caq.calcOffset(index++); - if (caq.lpElement(offset) != null) { - index--; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray2ReadWrite.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray2ReadWrite.java deleted file mode 100644 index 8089bc756..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray2ReadWrite.java +++ /dev/null @@ -1,88 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@BenchmarkMode({ Mode.Throughput }) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class CircularArray2ReadWrite { - public static final int CAPACITY = 1 << 15; - public static final Integer TOKEN = 1; - - private CircularArrayQueue2 caq = new CircularArrayQueue2(CAPACITY) { - @Override - public boolean offer(Integer e) { - return false; - } - - @Override - public Integer poll() { - return null; - } - - @Override - public Integer peek() { - return null; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public int size() { - return 0; - } - }; - - long index; - - @GenerateMicroBenchmark - public void offer() { - int offset = caq.calcOffset(index++); - caq.spElement(offset, TOKEN); - } - - @GenerateMicroBenchmark - public void poll() { - int offset = caq.calcOffset(index++); - if (caq.lpElement(offset) != null) { - index--; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray3ReadWrite.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray3ReadWrite.java deleted file mode 100644 index 56502c9b4..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray3ReadWrite.java +++ /dev/null @@ -1,88 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@BenchmarkMode({ Mode.Throughput }) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class CircularArray3ReadWrite { - public static final int CAPACITY = 1 << 15; - public static final Integer TOKEN = 1; - - private CircularArrayQueue2 caq = new CircularArrayQueue2(CAPACITY) { - @Override - public boolean offer(Integer e) { - return false; - } - - @Override - public Integer poll() { - return null; - } - - @Override - public Integer peek() { - return null; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public int size() { - return 0; - } - }; - - long index; - - @GenerateMicroBenchmark - public void offer() { - int offset = caq.calcOffset(index++); - caq.spElement(offset, TOKEN); - } - - @GenerateMicroBenchmark - public void poll() { - int offset = caq.calcOffset(index++); - if (caq.lpElement(offset) != null) { - index--; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray4ReadWrite.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray4ReadWrite.java deleted file mode 100644 index faf4d2507..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArray4ReadWrite.java +++ /dev/null @@ -1,89 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@BenchmarkMode({ Mode.Throughput }) -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@State(Scope.Thread) -public class CircularArray4ReadWrite { - public static final int CAPACITY = 1 << 15; - public static final Integer TOKEN = 1; - - private CircularArrayQueue4 caq = new CircularArrayQueue4(CAPACITY) { - - @Override - public Integer poll() { - return null; - } - - @Override - public Integer peek() { - return null; - } - - @Override - public boolean offer(Integer e) { - return false; - } - - @Override - public int size() { - return 0; - } - - @Override - public Iterator iterator() { - return null; - } - }; - - long index; - - @GenerateMicroBenchmark - public void offer() { - long offset = caq.calcOffset(index++); - caq.spElement(offset, TOKEN); - } - - @GenerateMicroBenchmark - public void poll() { - long offset = caq.calcOffset(index++); - if (caq.lpElement(offset) != null) { - index--; - } - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue1.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue1.java deleted file mode 100644 index 0be6df669..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue1.java +++ /dev/null @@ -1,50 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.AbstractQueue; - -public abstract class CircularArrayQueue1 extends AbstractQueue { - private final E[] buffer; - - @SuppressWarnings("unchecked") - public CircularArrayQueue1(int capacity) { - buffer = (E[]) new Object[capacity]; - } - - protected final void spElement(int offset, final E e) { - buffer[offset] = e; - } - - protected final E lpElement(final int offset) { - return buffer[offset]; - } - - protected final int calcOffset(final long index) { - return (int) (index % buffer.length); - } - - protected final int capacity() { - return buffer.length; - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue2.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue2.java deleted file mode 100644 index eff5d51d8..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue2.java +++ /dev/null @@ -1,56 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.AbstractQueue; - -public abstract class CircularArrayQueue2 extends AbstractQueue { - private final int capacity; - private final int mask; - private final E[] buffer; - - @SuppressWarnings("unchecked") - public CircularArrayQueue2(int capacity) { - this.capacity = Pow2.findNextPositivePowerOfTwo(capacity); - mask = capacity() - 1; - buffer = (E[]) new Object[capacity]; - } - - protected final void spElement(int offset, final E e) { - buffer[offset] = e; - } - - protected final E lpElement(final int offset) { - return buffer[offset]; - } - - protected final int calcOffset(final long index) { - // was: return (int) (index % buffer.length); - return ((int) index) & mask; - } - - protected final int capacity() { - // was: return buffer.length - return capacity; - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue3.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue3.java deleted file mode 100644 index afc1218b9..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue3.java +++ /dev/null @@ -1,59 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.AbstractQueue; -abstract class CircularArrayQueue3PrePad extends AbstractQueue { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; -} - -public abstract class CircularArrayQueue3 extends CircularArrayQueue3PrePad { - private final int capacity; - private final int mask; - private final E[] buffer; - - @SuppressWarnings("unchecked") - public CircularArrayQueue3(int capacity) { - this.capacity = Pow2.findNextPositivePowerOfTwo(capacity); - mask = capacity() - 1; - buffer = (E[]) new Object[capacity]; - } - - protected final void spElement(int offset, final E e) { - buffer[offset] = e; - } - - protected final E lpElement(final int offset) { - return buffer[offset]; - } - - protected final int calcOffset(final long index) { - return ((int) index) & mask; - } - - protected final int capacity() { - return capacity; - } - -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue4.java b/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue4.java deleted file mode 100644 index a5ac3032d..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/CircularArrayQueue4.java +++ /dev/null @@ -1,87 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.AbstractQueue; - -abstract class CircularArrayQueue4PrePad extends AbstractQueue { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; -} -public abstract class CircularArrayQueue4 extends CircularArrayQueue4PrePad { - private static final int BUFFER_PAD = 32; - protected static final long ARRAY_BASE; - protected static final int ELEMENT_SHIFT; - static { - final int scale = UnsafeAccess.UNSAFE.arrayIndexScale(Object[].class); - - if (4 == scale) { - ELEMENT_SHIFT = 2; - } else if (8 == scale) { - ELEMENT_SHIFT = 3; - } else { - throw new IllegalStateException("Unknown pointer size"); - } - ARRAY_BASE = UnsafeAccess.UNSAFE.arrayBaseOffset(Object[].class) + (BUFFER_PAD << ELEMENT_SHIFT); - } - private final int capacity; - private final long mask; - private final E[] buffer; - - @SuppressWarnings("unchecked") - public CircularArrayQueue4(int capacity) { - this.capacity = Pow2.findNextPositivePowerOfTwo(capacity); - mask = capacity() - 1; - // padding + size + padding - buffer = (E[]) new Object[this.capacity + BUFFER_PAD * 2]; - } - - protected final void spElement(final long offset, final E e) { - UnsafeAccess.UNSAFE.putObject(buffer, offset, e); - } - - @SuppressWarnings("unchecked") - protected final E lpElement(final long offset) { - return (E) UnsafeAccess.UNSAFE.getObject(buffer, offset); - } - protected final void soElement(final long offset, final E e) { - UnsafeAccess.UNSAFE.putOrderedObject(buffer, offset, e); - } - - @SuppressWarnings("unchecked") - protected final E lvElement(final long offset) { - return (E) UnsafeAccess.UNSAFE.getObjectVolatile(buffer, offset); - } - protected final long calcOffset(final long index) { - // inclusive of padding: - // array base + (padding * slot size) + ((index % capacity) * (slot size)) = - // ARRAY_BASE pre-calculated: ARRAY_BASE + ((index % capacity) * (slot size)) = - // capacity is power of 2: ARRAY_BASE + ((index & mask) * (slot size)) = - // slot size is a power of 2, replace with a shift of pre-calculated ELEMENT_SHIFT - return ARRAY_BASE + ((index & mask) << ELEMENT_SHIFT); - } - - protected final int capacity() { - return capacity; - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/Pow2.java b/libtest/src/main/java/psy/lob/saw/queues/common/Pow2.java deleted file mode 100644 index d1278866d..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/Pow2.java +++ /dev/null @@ -1,32 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class Pow2 { - public static int findNextPositivePowerOfTwo(final int value) { - return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); - } - public static boolean isPowerOf2(final int value){ - return (value & (value-1)) == 0; - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/SPSCQueueFactory.java b/libtest/src/main/java/psy/lob/saw/queues/common/SPSCQueueFactory.java deleted file mode 100644 index 8ae618073..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/common/SPSCQueueFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -package psy.lob.saw.queues.common; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; - -import psy.lob.saw.queues.lamport.LamportQueue1; -import psy.lob.saw.queues.ff.FastFlowQueue1; -import psy.lob.saw.queues.ff.FastFlowQueue2; -import psy.lob.saw.queues.lamport.LamportQueue2; -import psy.lob.saw.queues.lamport.LamportQueue3; -import psy.lob.saw.queues.lamport.LamportQueue4; -import psy.lob.saw.queues.thompson.ThompsonQueue1; -import psy.lob.saw.queues.thompson.ThompsonQueue2; -import psy.lob.saw.queues.thompson.ThompsonQueue3; -import psy.lob.saw.queues.lamport.LamportQueue5; - -public final class SPSCQueueFactory { - - public static Queue createQueue(int qId, int qScale) { - int qCapacity = 1 << qScale; - switch (qId) { - case 11: - return new ArrayBlockingQueue(qCapacity); - case 12: - return new ConcurrentLinkedQueue(); - case 21: - return new LamportQueue1(qCapacity); - case 22: - return new LamportQueue2(qCapacity); - case 23: - return new LamportQueue3(qCapacity); - case 24: - return new LamportQueue4(qCapacity); - case 25: - return new LamportQueue5(qCapacity); - case 31: - return new ThompsonQueue1(qCapacity); - case 32: - return new ThompsonQueue2(qCapacity); - case 33: - return new ThompsonQueue3(qCapacity); - case 41: - return new FastFlowQueue1(qCapacity); - case 42: - return new FastFlowQueue2(qCapacity); - default: - throw new IllegalArgumentException("Invalid option: " + qId); - } - } - -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue1.java b/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue1.java deleted file mode 100644 index cec976076..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue1.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 psy.lob.saw.queues.ff; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue4; - -/** - *
    - *
  • Inlined counters - *
  • Counters are padded - *
  • Data is padded - *
  • Class is pre-padded - *
  • Use Unsafe for array access - *
- */ -abstract class FastFlowQueue1L1Pad extends CircularArrayQueue4 { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public FastFlowQueue1L1Pad(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue1ConsumerIndex extends FastFlowQueue1L1Pad { - protected long consumerIndex; - - public FastFlowQueue1ConsumerIndex(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue1L3Pad extends FastFlowQueue1ConsumerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public FastFlowQueue1L3Pad(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue1ProducerIndex extends FastFlowQueue1L3Pad { - protected long producerIndex; - - public FastFlowQueue1ProducerIndex(int capacity) { - super(capacity); - } -} - -public final class FastFlowQueue1 extends FastFlowQueue1ProducerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - public FastFlowQueue1(int capacity) { - super(capacity); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long offset = calcOffset(producerIndex); - if (null != lvElement(offset)) { // LoadLoad - return false; - } - soElement(offset, e); //StoreStore - producerIndex++; - return true; - } - - @Override - public E poll() { - final long offset = calcOffset(consumerIndex); - final E e = lvElement(offset); // LoadLoad - if (null == e) { - return null; - } - soElement(offset, null); // StoreStore - consumerIndex++; - return e; - } - @Override - public E peek() { - final long offset = calcOffset(consumerIndex); - return lvElement(offset); - } - - @Override - public int size() { - // This won't work very well :( - return (int) (producerIndex - consumerIndex); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue2.java b/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue2.java deleted file mode 100644 index e83820a52..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/ff/FastFlowQueue2.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 psy.lob.saw.queues.ff; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue4; - -/** - *
    - *
  • Inlined counters - *
  • Counters are padded - *
  • Data is padded - *
  • Class is pre-padded - *
  • Use Unsafe for array access - *
- */ -abstract class FastFlowQueue2L1Pad extends CircularArrayQueue4 { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public FastFlowQueue2L1Pad(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue2TailField extends FastFlowQueue2L1Pad { - protected long consumerIndex; - - public FastFlowQueue2TailField(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue2L3Pad extends FastFlowQueue2TailField { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public FastFlowQueue2L3Pad(int capacity) { - super(capacity); - } -} - -abstract class FastFlowQueue2HeadField extends FastFlowQueue2L3Pad { - protected long producerIndex; - protected long lookAheadCache; - - public FastFlowQueue2HeadField(int capacity) { - super(capacity); - } - -} - -public final class FastFlowQueue2 extends FastFlowQueue2HeadField { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - protected static final int OFFER_LOOK_AHEAD = Integer.getInteger("offer.batch.size", 4096); - - public FastFlowQueue2(int capacity) { - super(Math.min(capacity, OFFER_LOOK_AHEAD * 2)); - } - - private void incConsumerIndex() { - consumerIndex++; - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - if (lookAheadCache < producerIndex) { - long lookAheadOffset = calcOffset(producerIndex + OFFER_LOOK_AHEAD); - if (null != lvElement(lookAheadOffset)) { // LoadLoad - return false; - } else { - lookAheadCache = producerIndex + OFFER_LOOK_AHEAD; - } - } - final long offset = calcOffset(producerIndex); - soElement(offset, e); // StoreStore - producerIndex++; - return true; - } - - @Override - public E poll() { - final long offset = calcOffset(consumerIndex); - final E e = lvElement(offset); // LoadLoad - if (null == e) { - return null; - } - soElement(offset, null); // StoreStore - incConsumerIndex(); - return e; - } - - @Override - public E peek() { - final long offset = calcOffset(consumerIndex); - return lvElement(offset); - } - - @Override - public int size() { - // This won't work very well :( - return (int) (producerIndex - consumerIndex); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue1.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue1.java deleted file mode 100644 index 878582b53..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue1.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue1; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
- */ - -public final class LamportQueue1 extends CircularArrayQueue1 { - private volatile long producerIndex = 0; - private volatile long consumerIndex = 0; - public LamportQueue1(final int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex; // LoadLoad - } - - private void svProducerIndex(long producerIndex) { - this.producerIndex = producerIndex; // StoreLoad - } - - private long lvConsumerIndex() { - return consumerIndex; // LoadLoad - } - - private void svConsumerIndex(long consumerIndex) { - this.consumerIndex = consumerIndex; // StoreLoad - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); // LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lvConsumerIndex() <= wrapPoint) { // LoadLoad - return false; - } - - final int offset = calcOffset(currentProducerIndex); - spElement(offset, e); - svProducerIndex(currentProducerIndex + 1); // StoreLoad - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lvProducerIndex()) { // LoadLoad - return null; - } - - final int offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - svConsumerIndex(currentConsumerIndex + 1); // StoreLoad - return e; - } - - @Override - public E peek() { - final int offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue2.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue2.java deleted file mode 100644 index d6e896bd2..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue2.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicLong; - -import psy.lob.saw.queues.common.CircularArrayQueue1; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Replacing the long fields with AtomicLong and using lazySet instead of - * volatile assignment. - *
- */ -public final class LamportQueue2 extends CircularArrayQueue1 { - private final AtomicLong producerIndex = new AtomicLong(); - private final AtomicLong consumerIndex = new AtomicLong(); - - public LamportQueue2(final int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex.get(); - } - - private void soProducerIndex(long index) { - producerIndex.lazySet(index); - } - - private long lvConsumerIndex() { - return consumerIndex.get(); - } - - private void soConsumerIndex(long index) { - consumerIndex.lazySet(index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex();// LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lvConsumerIndex() <= wrapPoint) { // LoadLoad - return false; - } - - final int offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); // StoreStore - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lvProducerIndex()) { // LoadLoad - return null; - } - - final int offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); // StoreStore - return e; - } - - @Override - public E peek() { - final int offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue3.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue3.java deleted file mode 100644 index 58e35d92b..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue3.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicLong; - -import psy.lob.saw.queues.common.CircularArrayQueue2; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Replacing the long fields with AtomicLong and using lazySet instead of - * volatile assignment. - *
  • Using the power of 2 mask, forcing the capacity to next power of 2. - *
- */ -public final class LamportQueue3 extends CircularArrayQueue2 { - private final AtomicLong producerIndex = new AtomicLong(); - private final AtomicLong consumerIndex = new AtomicLong(); - public LamportQueue3(final int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex.get(); - } - - private void soProducerIndex(long index) { - producerIndex.lazySet(index); - } - - private long lvConsumerIndex() { - return consumerIndex.get(); - } - - private void soConsumerIndex(long index) { - consumerIndex.lazySet(index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); // LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lvConsumerIndex() <= wrapPoint) { // LoadLoad - return false; - } - - final int offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); // StoreStore - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lvProducerIndex()) { // LoadLoad - return null; - } - - final int offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); // StoreStore - return e; - } - - @Override - public E peek() { - final int offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue4.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue4.java deleted file mode 100644 index dd50a4133..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue4.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue3; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Replacing the long fields with AtomicLong and using lazySet instead of - * volatile assignment. - *
  • Using the power of 2 mask, forcing the capacity to next power of 2. - *
  • Using a fully padded 'AtomicLong' like variable - *
  • Fully padded circular array - *
- */ -abstract class LamportQueue4Fields extends CircularArrayQueue3 { - protected final VolatileLongCell producerIndex = new VolatileLongCell(); - protected final VolatileLongCell consumerIndex = new VolatileLongCell(); - - public LamportQueue4Fields(int capacity) { - super(capacity); - } - -} -public final class LamportQueue4 extends LamportQueue4Fields { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - public LamportQueue4(final int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex.get(); - } - - private void soProducerIndex(long index) { - producerIndex.lazySet(index); - } - - private long lvConsumerIndex() { - return consumerIndex.get(); - } - - private void soConsumerIndex(long index) { - consumerIndex.lazySet(index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); // LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lvConsumerIndex() <= wrapPoint) { // LoadLoad - return false; - } - - final int offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); // StoreStore - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lvProducerIndex()) { // LoadLoad - return null; - } - - final int offset= calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); // StoreStore - return e; - } - - @Override - public E peek() { - final int offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue5.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue5.java deleted file mode 100644 index 7f4b73a8c..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/LamportQueue5.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue4; -import psy.lob.saw.queues.common.UnsafeAccess; - -/** - *
    - *
  • Inlined counters - *
  • Counters are padded - *
  • Data is padded - *
  • Class is pre-padded - *
  • Use Unsafe for array access - *
- */ -abstract class LamportQueue5L1Pad extends CircularArrayQueue4 { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public LamportQueue5L1Pad(int capacity) { - super(capacity); - } -} - -abstract class LamportQueue5ConsumerIndex extends LamportQueue5L1Pad { - protected volatile long consumerIndex; - - public LamportQueue5ConsumerIndex(int capacity) { - super(capacity); - } -} - -abstract class LamportQueue5L3Pad extends LamportQueue5ConsumerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public LamportQueue5L3Pad(int capacity) { - super(capacity); - } -} - -abstract class LamportQueue5ProducerIndex extends LamportQueue5L3Pad { - protected volatile long producerIndex; - - public LamportQueue5ProducerIndex(int capacity) { - super(capacity); - } -} - -public final class LamportQueue5 extends LamportQueue5ProducerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - private final static long CONSUMER_INDEX_OFFSET; - private final static long PRODUCER_INDEX_OFFSET; - static { - try { - CONSUMER_INDEX_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(LamportQueue5ConsumerIndex.class.getDeclaredField("consumerIndex")); - PRODUCER_INDEX_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(LamportQueue5ProducerIndex.class.getDeclaredField("producerIndex")); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - public LamportQueue5(int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex; - } - - private void soProducerIndex(long index) { - UnsafeAccess.UNSAFE.putOrderedLong(this, PRODUCER_INDEX_OFFSET, index); - } - - private long lvConsumerIndex() { - return consumerIndex; - } - - private void soConsumerIndex(long index) { - UnsafeAccess.UNSAFE.putOrderedLong(this, CONSUMER_INDEX_OFFSET, index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); // LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lvConsumerIndex() <= wrapPoint) { // LoadLoad - return false; - } - - final long offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); // StoreStore - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lvProducerIndex()) { // LoadLoad - return null; - } - - final long offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); // StoreStore - return e; - } - - @Override - public E peek() { - final long offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/lamport/VolatileLongCell.java b/libtest/src/main/java/psy/lob/saw/queues/lamport/VolatileLongCell.java deleted file mode 100644 index 02d0feea0..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/lamport/VolatileLongCell.java +++ /dev/null @@ -1,57 +0,0 @@ -package psy.lob.saw.queues.lamport; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import psy.lob.saw.queues.common.UnsafeAccess; - - -abstract class VolatileLongCellPrePad{long p0,p1,p2,p3,p4,p5,p6;} -abstract class VolatileLongCellValue extends VolatileLongCellPrePad { - protected volatile long value; -} -public final class VolatileLongCell extends VolatileLongCellValue { - long p10,p11,p12,p13,p14,p15,p16; - private final static long VALUE_OFFSET; - static { - try { - VALUE_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(VolatileLongCellValue.class.getDeclaredField("value")); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - public VolatileLongCell(){ - this(0L); - } - public VolatileLongCell(long v){ - lazySet(v); - } - public void lazySet(long v) { - UnsafeAccess.UNSAFE.putOrderedLong(this, VALUE_OFFSET, v); - } - public void set(long v){ - this.value = v; - } - public long get(){ - return this.value; - } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/thompson/LongCell.java b/libtest/src/main/java/psy/lob/saw/queues/thompson/LongCell.java deleted file mode 100644 index a2644afe0..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/thompson/LongCell.java +++ /dev/null @@ -1,34 +0,0 @@ -package psy.lob.saw.queues.thompson; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - -abstract class LongCellP0{long p0,p1,p2,p3,p4,p5,p6;} -abstract class LongCellValue extends LongCellP0 { - protected long value; -} -public final class LongCell extends LongCellValue { - long p10,p11,p12,p13,p14,p15,p16; - public void set(long v){ this.value = v; } - public long get(){ return this.value; } -} diff --git a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue1.java b/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue1.java deleted file mode 100644 index 4be4629c7..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue1.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.thompson; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.lamport.VolatileLongCell; -import psy.lob.saw.queues.common.CircularArrayQueue3; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Replacing the long fields with AtomicLong and using lazySet instead of - * volatile assignment. - *
  • Using the power of 2 mask, forcing the capacity to next power of 2. - *
  • Using a fully padded 'AtomicLong' like variable - *
  • Fully padded circular array - *
  • Use fully padded index cache fields - *
- */ -abstract class ThompsonQueue1Fields extends CircularArrayQueue3 { - protected final VolatileLongCell producerIndex = new VolatileLongCell(); - protected final VolatileLongCell consumerIndex = new VolatileLongCell(); - protected final LongCell producerIndexCache = new LongCell(); - protected final LongCell consumerIndexCache = new LongCell(); - - public ThompsonQueue1Fields(int capacity) { - super(capacity); - } - -} -public final class ThompsonQueue1 extends ThompsonQueue1Fields { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - public ThompsonQueue1(final int capacity) { - super(capacity); - } - private long lvProducerIndex() { - return producerIndex.get(); - } - - private void soProducerIndex(long index) { - producerIndex.lazySet(index); - } - - private long lvConsumerIndex() { - return consumerIndex.get(); - } - - private void soConsumerIndex(long index) { - consumerIndex.lazySet(index); - } - - private long lpConsumerIndexCache() { - return consumerIndexCache.get(); - } - - private void spConsumerIndexCache(long index) { - consumerIndexCache.set(index); - } - - private long lpProducerIndexCache() { - return producerIndexCache.get(); - } - - private void spProducerIndexCache(long index) { - producerIndexCache.set(index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); // LoadLoad - final long wrapPoint = currentProducerIndex - capacity(); - if (lpConsumerIndexCache() <= wrapPoint) { - spConsumerIndexCache(lvConsumerIndex()); // LoadLoad - if (lpConsumerIndexCache() <= wrapPoint) { - return false; - } - } - - final int offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); // StoreStore - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); // LoadLoad - if (currentConsumerIndex >= lpProducerIndexCache()) { - spProducerIndexCache(lvProducerIndex()); // LoadLoad - if (currentConsumerIndex >= lpProducerIndexCache()) { - return null; - } - } - - final int offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); // StoreStore - return e; - } - - @Override - public E peek() { - final int offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue2.java b/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue2.java deleted file mode 100644 index 3841b58d6..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue2.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2012 Real Logic Ltd. - * - * 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 psy.lob.saw.queues.thompson; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.lamport.VolatileLongCell; -import psy.lob.saw.queues.common.CircularArrayQueue4; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Replacing the long fields with AtomicLong and using lazySet instead of - * volatile assignment. - *
  • Using the power of 2 mask, forcing the capacity to next power of 2. - *
  • Using a fully padded 'AtomicLong' like variable - *
  • Fully padded circular array - *
  • Use fully padded index cache fields - *
  • Unsafe array access - *
- */ -abstract class ThompsonQueue2Fields extends CircularArrayQueue4 { - protected final VolatileLongCell producerIndex = new VolatileLongCell(); - protected final VolatileLongCell consumerIndex = new VolatileLongCell(); - protected final LongCell producerIndexCache = new LongCell(); - protected final LongCell consumerIndexCache = new LongCell(); - - public ThompsonQueue2Fields(int capacity) { - super(capacity); - } - -} -public final class ThompsonQueue2 extends ThompsonQueue2Fields { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - public ThompsonQueue2(final int capacity) { - super(capacity); - } - private long lvProducerIndex() { - return producerIndex.get(); - } - - private void soProducerIndex(long index) { - producerIndex.lazySet(index); - } - - private long lvConsumerIndex() { - return consumerIndex.get(); - } - - private void soConsumerIndex(long index) { - consumerIndex.lazySet(index); - } - - private long lpConsumerIndexCache() { - return consumerIndexCache.get(); - } - - private void spConsumerIndexCache(long index) { - consumerIndexCache.set(index); - } - - private long lpProducerIndexCache() { - return producerIndexCache.get(); - } - - private void spProducerIndexCache(long index) { - producerIndexCache.set(index); - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); - final long wrapPoint = currentProducerIndex - capacity(); - if (lpConsumerIndexCache() <= wrapPoint) { - spConsumerIndexCache(lvConsumerIndex()); - if (lpConsumerIndexCache() <= wrapPoint) { - return false; - } - } - - final long offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); - if (currentConsumerIndex >= lpProducerIndexCache()) { - spProducerIndexCache(lvProducerIndex()); - if (currentConsumerIndex >= lpProducerIndexCache()) { - return null; - } - } - - final long offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); - return e; - } - - @Override - public E peek() { - final long offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue3.java b/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue3.java deleted file mode 100644 index 48af31e8b..000000000 --- a/libtest/src/main/java/psy/lob/saw/queues/thompson/ThompsonQueue3.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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 psy.lob.saw.queues.thompson; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; - -import psy.lob.saw.queues.common.CircularArrayQueue4; -import psy.lob.saw.queues.common.UnsafeAccess; - -/** - *
    - *
  • Lock free, observing single writer principal (except for buffer). - *
  • Using the power of 2 mask, forcing the capacity to next power of 2. - *
  • Using a fully padded 'AtomicLong' like variable - *
  • Fully padded circular array - *
  • Use fully padded index cache fields - *
  • Unsafe array access - *
  • Inline padded atomic counters - *
- */ -abstract class ThompsonQueue3L1Pad extends CircularArrayQueue4 { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public ThompsonQueue3L1Pad(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3ConsumerIndex extends ThompsonQueue3L1Pad { - protected volatile long consumerIndex; - - public ThompsonQueue3ConsumerIndex(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3L2Pad extends ThompsonQueue3ConsumerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - - public ThompsonQueue3L2Pad(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3HeadCache extends ThompsonQueue3L2Pad { - protected long producerIndexCache; - - public ThompsonQueue3HeadCache(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3L3Pad extends ThompsonQueue3HeadCache { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - - public ThompsonQueue3L3Pad(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3ProducerIndex extends ThompsonQueue3L3Pad { - protected volatile long producerIndex; - - public ThompsonQueue3ProducerIndex(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3L4Pad extends ThompsonQueue3ProducerIndex { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - - public ThompsonQueue3L4Pad(int capacity) { - super(capacity); - } -} - -abstract class ThompsonQueue3ConsumerCache extends ThompsonQueue3L4Pad { - protected long consumerIndexCache; - - public ThompsonQueue3ConsumerCache(int capacity) { - super(capacity); - } - -} - -public final class ThompsonQueue3 extends ThompsonQueue3ConsumerCache { - protected long p00, p01, p02, p03, p04, p05, p06, p07; - protected long p10, p11, p12, p13, p14, p15, p16, p17; - private final static long CONSUMER_INDEX_OFFSET; - private final static long PRODUCER_INDEX_OFFSET; - static { - try { - CONSUMER_INDEX_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(ThompsonQueue3ConsumerIndex.class.getDeclaredField("consumerIndex")); - PRODUCER_INDEX_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(ThompsonQueue3ProducerIndex.class.getDeclaredField("producerIndex")); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - public ThompsonQueue3(int capacity) { - super(capacity); - } - - private long lvProducerIndex() { - return producerIndex; - } - - private void soProducerIndex(long index) { - UnsafeAccess.UNSAFE.putOrderedLong(this, PRODUCER_INDEX_OFFSET, index); - } - - private long lvConsumerIndex() { - return consumerIndex; - } - - private void soConsumerIndex(long index) { - UnsafeAccess.UNSAFE.putOrderedLong(this, CONSUMER_INDEX_OFFSET, index); - } - - private long lpConsumerIndexCache() { - return consumerIndexCache; - } - - private void spConsumerIndexCache(long index) { - consumerIndexCache = index; - } - - private long lpProducerIndexCache() { - return producerIndexCache; - } - - private void spProducerIndexCache(long index) { - producerIndexCache = index; - } - - @Override - public boolean offer(final E e) { - if (null == e) { - throw new NullPointerException("Null is not a valid element"); - } - - final long currentProducerIndex = lvProducerIndex(); - final long wrapPoint = currentProducerIndex - capacity(); - if (lpConsumerIndexCache() <= wrapPoint) { - spConsumerIndexCache(lvConsumerIndex()); - if (lpConsumerIndexCache() <= wrapPoint) { - return false; - } - } - - final long offset = calcOffset(currentProducerIndex); - spElement(offset, e); - soProducerIndex(currentProducerIndex + 1); - return true; - } - - @Override - public E poll() { - final long currentConsumerIndex = lvConsumerIndex(); - if (currentConsumerIndex >= lpProducerIndexCache()) { - spProducerIndexCache(lvProducerIndex()); - if (currentConsumerIndex >= lpProducerIndexCache()) { - return null; - } - } - - final long offset = calcOffset(currentConsumerIndex); - final E e = lpElement(offset); - spElement(offset, null); - soConsumerIndex(currentConsumerIndex + 1); - return e; - } - - @Override - public E peek() { - final long offset = calcOffset(lvConsumerIndex()); - return lpElement(offset); - } - - @Override - public int size() { - return (int) (lvProducerIndex() - lvConsumerIndex()); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } -} diff --git a/libtest/src/main/java/romix/scala/None.java b/libtest/src/main/java/romix/scala/None.java deleted file mode 100644 index 3fb984e17..000000000 --- a/libtest/src/main/java/romix/scala/None.java +++ /dev/null @@ -1,34 +0,0 @@ -package romix.scala; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Mimic None in Scala - * - * @author Roman Levenstein - * - * @param - */ -public class None extends Option { - -} diff --git a/libtest/src/main/java/romix/scala/Option.java b/libtest/src/main/java/romix/scala/Option.java deleted file mode 100644 index 707f2b3e8..000000000 --- a/libtest/src/main/java/romix/scala/Option.java +++ /dev/null @@ -1,48 +0,0 @@ -package romix.scala; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Mimic Option in Scala - * - * @author Roman Levenstein - * - * @param - */ -@SuppressWarnings({"rawtypes", "unchecked"}) -public class Option { - static None none = new None(); - public static Option makeOption(V o){ - if(o!=null) - return new Some(o); - else - return (Option)none; - } - - public static Option makeOption(){ - return (Option)none; - } - public boolean nonEmpty () { - return false; - } -} diff --git a/libtest/src/main/java/romix/scala/Some.java b/libtest/src/main/java/romix/scala/Some.java deleted file mode 100644 index 18218c045..000000000 --- a/libtest/src/main/java/romix/scala/Some.java +++ /dev/null @@ -1,45 +0,0 @@ -package romix.scala; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Mimic Some in Scala - * - * @author Roman Levenstein - * - * @param - */ -public class Some extends Option { - final V value; - public Some(V v) { - value = v; - } - - public V get() { - return value; - } - - public boolean nonEmpty () { - return value != null; - } -} diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/BasicNode.java b/libtest/src/main/java/romix/scala/collection/concurrent/BasicNode.java deleted file mode 100644 index 6965124ae..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/BasicNode.java +++ /dev/null @@ -1,42 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - - - - - -abstract class BasicNode { - - public abstract String string(int lev); - -} \ No newline at end of file diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/CNodeBase.java b/libtest/src/main/java/romix/scala/collection/concurrent/CNodeBase.java deleted file mode 100644 index 18e128874..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/CNodeBase.java +++ /dev/null @@ -1,57 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - - -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - - - -abstract class CNodeBase extends MainNode { - - public static final AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(CNodeBase.class, "csize"); - - public volatile int csize = -1; - - public boolean CAS_SIZE(int oldval, int nval) { - return updater.compareAndSet(this, oldval, nval); - } - - public void WRITE_SIZE(int nval) { - updater.set(this, nval); - } - - public int READ_SIZE() { - return updater.get(this); - } - -} \ No newline at end of file diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/Gen.java b/libtest/src/main/java/romix/scala/collection/concurrent/Gen.java deleted file mode 100644 index 360510f55..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/Gen.java +++ /dev/null @@ -1,39 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - - - - - -final class Gen { -} \ No newline at end of file diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/INodeBase.java b/libtest/src/main/java/romix/scala/collection/concurrent/INodeBase.java deleted file mode 100644 index 8aae9a054..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/INodeBase.java +++ /dev/null @@ -1,57 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - - - -abstract class INodeBase extends BasicNode { - - public static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(INodeBase.class, MainNode.class, "mainnode"); - - public static final Object RESTART = new Object(); - - public volatile MainNode mainnode = null; - - public final Gen gen; - - public INodeBase(Gen generation) { - gen = generation; - } - - public BasicNode prev() { - return null; - } - -} \ No newline at end of file diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/ListMap.java b/libtest/src/main/java/romix/scala/collection/concurrent/ListMap.java deleted file mode 100644 index 4ff7e36f6..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/ListMap.java +++ /dev/null @@ -1,251 +0,0 @@ -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Iterator; -import java.util.Map.Entry; - -import romix.scala.Option; - -/** - * Mimic immutable ListMap in Scala - * - * @author Roman Levenstein - * - * @param - */ -abstract class ListMap { - - ListMap next; - - static ListMap map(K k, V v, ListMap tail){ - return new Node (k, v, tail); - } - - static ListMap map(K k, V v){ - return new Node (k, v, null); - } - - static ListMap map(K k1, V v1, K k2, V v2){ - return new Node (k1, v1, new Node(k2,v2, null)); - } - - public abstract int size (); - - public abstract boolean isEmpty() ; - - abstract public boolean contains(K k, V v); - - abstract public boolean contains(K key); - - abstract public Option get (K key); - - abstract public ListMap add (K key, V value); - - abstract public ListMap remove (K key); - - abstract public Iterator> iterator(); - - - static class EmptyListMap extends ListMap { - public ListMap add (K key, V value) { - return ListMap.map(key, value, null); - } - - public boolean contains(K k, V v) { - return false; - } - - public boolean contains(K k) { - return false; - } - - public ListMap remove(K key) { - return this; - } - - @Override - public int size () { - return 0; - } - - @Override - public boolean isEmpty () { - return true; - } - - @Override - public Option get (K key) { - return Option.makeOption (null); - } - - @Override - public Iterator> iterator () { - return new EmptyListMapIterator (); - } - - static class EmptyListMapIterator implements Iterator> { - - @Override - public boolean hasNext () { - return false; - } - - @Override - public Entry next () { - return null; - } - - @Override - public void remove () { - throw new RuntimeException("Operation not supported"); - } - - } - } - - static class Node extends ListMap { - final K k; - final V v; - - Node(K k, V v, ListMap next) { - this.k = k; - this.v = v; - this.next = next; - } - - public ListMap add (K key, V value) { - return ListMap.map(key, value, remove (key)); - } - - public boolean contains(K k, V v) { - if(k.equals (this.k) && v.equals (this.v)) - return true; - else if(next != null) - return next.contains (k, v); - return false; - } - - public boolean contains(K k) { - if(k.equals (this.k)) - return true; - else if(next != null) - return next.contains (k); - return false; - } - - public ListMap remove(K key) { - if(!contains(key)) - return this; - else - return remove0(key); - } - - private ListMap remove0 (K key) { - ListMap n = this; - ListMap newN = null; - ListMap lastN = null; - while (n != null) { - if(n instanceof EmptyListMap) { - newN.next = n; - break; - } - Node nn = (Node)n; - if (key.equals (nn.k)) { - n = n.next; - continue; - } else { - if (newN != null) { - lastN.next = ListMap.map (nn.k, nn.v, null); - lastN = lastN.next; - } else { - newN = ListMap.map (nn.k, nn.v, null); - lastN = newN; - } - } - n = n.next; - } - return newN; - } - - @Override - public int size () { - if(next == null) - return 1; - else - return 1+next.size (); - } - - @Override - public boolean isEmpty () { - return false; - } - - @Override - public Option get (K key) { - if(key.equals (k)) - return Option.makeOption (v); - if(next != null) - return next.get (key); - return Option.makeOption (null); - } - - - @Override - public Iterator> iterator () { - return new NodeIterator (this); - } - - static class NodeIterator implements Iterator> { - ListMap n; - - public NodeIterator (Node n) { - this.n = n; - } - - @Override - public boolean hasNext () { -// return n!= null && n.next != null && !(n.next instanceof EmptyListMap); - return n!= null && !(n instanceof EmptyListMap); - } - - @Override - public Entry next () { - if (n instanceof Node) { - Node nn = (Node) n; - Pair res = new Pair (nn.k, nn.v); - n = n.next; - return res; - } else { - return null; - } - } - - @Override - public void remove () { - throw new RuntimeException("Operation not supported"); - } - - } - } -} diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/MainNode.java b/libtest/src/main/java/romix/scala/collection/concurrent/MainNode.java deleted file mode 100644 index 67ff7ac07..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/MainNode.java +++ /dev/null @@ -1,58 +0,0 @@ -/* __ *\ - ** ________ ___ / / ___ Scala API ** - ** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL ** - ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** - ** /____/\___/_/ |_/____/_/ | | ** - ** |/ ** -\* */ - -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - -abstract class MainNode extends BasicNode { - - public static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater (MainNode.class, MainNode.class, "prev"); - - public volatile MainNode prev = null; - - public abstract int cachedSize (Object ct); - - public boolean CAS_PREV (MainNode oldval, MainNode nval) { - return updater.compareAndSet (this, oldval, nval); - } - - public void WRITE_PREV (MainNode nval) { - updater.set (this, nval); - } - - // do we need this? unclear in the javadocs... - // apparently not - volatile reads are supposed to be safe - // regardless of whether there are concurrent ARFU updates - public MainNode READ_PREV () { - return updater.get (this); - } - -} diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/Pair.java b/libtest/src/main/java/romix/scala/collection/concurrent/Pair.java deleted file mode 100644 index 1a3946ca3..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/Pair.java +++ /dev/null @@ -1,62 +0,0 @@ -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Map; - -/*** - * Helper class simulating a tuple of 2 elements in Scala - * - * @author Roman Levenstein - * - * @param - * @param - */ -public class Pair implements Map.Entry { - - final K k; - final V v; - - Pair (K k, V v) { - this.k = k; - this.v = v; - } - - @Override - public K getKey () { - // TODO Auto-generated method stub - return k; - } - - @Override - public V getValue () { - // TODO Auto-generated method stub - return v; - } - - @Override - public V setValue (V value) { - throw new RuntimeException ("Operation not supported"); - } - -} diff --git a/libtest/src/main/java/romix/scala/collection/concurrent/TrieMap.java b/libtest/src/main/java/romix/scala/collection/concurrent/TrieMap.java deleted file mode 100644 index 58763f63f..000000000 --- a/libtest/src/main/java/romix/scala/collection/concurrent/TrieMap.java +++ /dev/null @@ -1,1863 +0,0 @@ -package romix.scala.collection.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.Field; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - -import romix.scala.None; -import romix.scala.Option; -import romix.scala.Some; - -/*** - * This is a port of Scala's TrieMap class from the Scala Collections library. - * - * @author Roman Levenstein - * - * @param - * @param - */ -@SuppressWarnings({"unchecked", "rawtypes", "unused"}) -public class TrieMap extends AbstractMap implements ConcurrentMap, Serializable { - private static final AtomicReferenceFieldUpdater ROOT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(TrieMap.class, Object.class, "root"); - private static final long serialVersionUID = 1L; - private static final Field READONLY_FIELD; - - static { - final Field f; - try { - f = TrieMap.class.getDeclaredField("readOnly"); - } catch (NoSuchFieldException e) { - throw new ExceptionInInitializerError(e); - } catch (SecurityException e) { - throw new ExceptionInInitializerError(e); - } - f.setAccessible(true); - READONLY_FIELD = f; - } - - /** - * EntrySet - */ - private transient final EntrySet entrySet = new EntrySet (); - - public static TrieMap empty () { - return new TrieMap(); - } - - // static class MangledHashing extends Hashing { - // int hash(K k) { - // return util.hashing.byteswap32(k); - // } - // } - - static class INode extends INodeBase { - static final Object KEY_PRESENT = new Object (); - static final Object KEY_ABSENT = new Object (); - - static INode newRootNode () { - Gen gen = new Gen(); - CNode cn = new CNode (0, new BasicNode[] {}, gen); - return new INode(cn, gen); - } - - public INode (MainNode bn, Gen g) { - super (g); - WRITE (bn); - } - - public INode (Gen g) { - this (null, g); - } - - final void WRITE (final MainNode nval) { - updater.set (this, nval); - } - - final boolean CAS (final MainNode old, final MainNode n) { - return updater.compareAndSet (this, old, n); - } - - final MainNode gcasRead (final TrieMap ct) { - return GCAS_READ (ct); - } - - final MainNode GCAS_READ (TrieMap ct) { - MainNode m = /* READ */mainnode; - MainNode prevval = /* READ */m.prev; - if (prevval == null) - return m; - else - return GCAS_Complete (m, ct); - } - - private MainNode GCAS_Complete (MainNode m, final TrieMap ct) { - while (true) { - if (m == null) - return null; - else { - // complete the GCAS - MainNode prev = /* READ */m.prev; - INode ctr = ct.readRoot (true); - - if (prev == null) - return m; - - if (prev instanceof FailedNode) { - // try to commit to previous value - FailedNode fn = (FailedNode) prev; - if (CAS (m, fn.prev)) - return fn.prev; - else { - // Tailrec - // return GCAS_Complete (/* READ */mainnode, ct); - m = /* READ */mainnode; - continue; - } - } else if (prev instanceof MainNode) { - // Assume that you've read the root from the generation - // G. - // Assume that the snapshot algorithm is correct. - // ==> you can only reach nodes in generations <= G. - // ==> `gen` is <= G. - // We know that `ctr.gen` is >= G. - // ==> if `ctr.gen` = `gen` then they are both equal to - // G. - // ==> otherwise, we know that either `ctr.gen` > G, - // `gen` < - // G, - // or both - if ((ctr.gen == gen) && ct.nonReadOnly ()) { - // try to commit - if (m.CAS_PREV (prev, null)) - return m; - else { - // return GCAS_Complete (m, ct); - // tailrec - continue; - } - } else { - // try to abort - m.CAS_PREV (prev, new FailedNode (prev)); - return GCAS_Complete (/* READ */mainnode, ct); - } - } - } - throw new RuntimeException ("Should not happen"); - } - } - - final boolean GCAS (final MainNode old, final MainNode n, final TrieMap ct) { - n.WRITE_PREV (old); - if (CAS (old, n)) { - GCAS_Complete (n, ct); - return /* READ */n.prev == null; - } else - return false; - } - - private boolean equal (final K k1, final K k2, final TrieMap ct) { - return ct.equality ().equiv (k1, k2); - } - - private INode inode (final MainNode cn) { - INode nin = new INode (gen); - nin.WRITE (cn); - return nin; - } - - final INode copyToGen (final Gen ngen, final TrieMap ct) { - INode nin = new INode (ngen); - MainNode main = GCAS_READ (ct); - nin.WRITE (main); - return nin; - } - - /** - * Inserts a key value pair, overwriting the old pair if the keys match. - * - * @return true if successful, false otherwise - */ - final boolean rec_insert (final K k, final V v, final int hc, final int lev, final INode parent, final Gen startgen, final TrieMap ct) { - while (true) { - MainNode m = GCAS_READ (ct); // use -Yinline! - - if (m instanceof CNode) { - // 1) a multiway node - CNode cn = (CNode) m; - int idx = (hc >>> lev) & 0x1f; - int flag = 1 << idx; - int bmp = cn.bitmap; - int mask = flag - 1; - int pos = Integer.bitCount (bmp & mask); - if ((bmp & flag) != 0) { - // 1a) insert below - BasicNode cnAtPos = cn.array [pos]; - if (cnAtPos instanceof INode) { - INode in = (INode) cnAtPos; - if (startgen == in.gen) - return in.rec_insert (k, v, hc, lev + 5, this, startgen, ct); - else { - if (GCAS (cn, cn.renewed (startgen, ct), ct)) { - // return rec_insert (k, v, hc, lev, parent, - // startgen, ct); - // tailrec - continue; - } else - return false; - } - } else if (cnAtPos instanceof SNode) { - SNode sn = (SNode) cnAtPos; - if (sn.hc == hc && equal ((K) sn.k, k, ct)) - return GCAS (cn, cn.updatedAt (pos, new SNode (k, v, hc), gen), ct); - else { - CNode rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct); - MainNode nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen); - return GCAS (cn, nn, ct); - } - } - } else { - CNode rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct); - MainNode ncnode = rn.insertedAt (pos, flag, new SNode (k, v, hc), gen); - return GCAS (cn, ncnode, ct); - } - } else if (m instanceof TNode) { - clean (parent, ct, lev - 5); - return false; - } else if (m instanceof LNode) { - LNode ln = (LNode) m; - MainNode nn = ln.inserted (k, v); - return GCAS (ln, nn, ct); - } - - throw new RuntimeException ("Should not happen"); - } - } - - /** - * Inserts a new key value pair, given that a specific condition is met. - * - * @param cond - * null - don't care if the key was there - * KEY_ABSENT - key wasn't there - * KEY_PRESENT - key was there - * other value `v` - key must be bound to `v` - * @return null if unsuccessful, Option[V] otherwise (indicating - * previous value bound to the key) - */ - final Option rec_insertif (final K k, final V v, final int hc, final Object cond, final int lev, final INode parent, final Gen startgen, final TrieMap ct) { - while (true) { - MainNode m = GCAS_READ (ct); // use -Yinline! - - if (m instanceof CNode) { - // 1) a multiway node - CNode cn = (CNode) m; - int idx = (hc >>> lev) & 0x1f; - int flag = 1 << idx; - int bmp = cn.bitmap; - int mask = flag - 1; - int pos = Integer.bitCount (bmp & mask); - - if ((bmp & flag) != 0) { - // 1a) insert below - BasicNode cnAtPos = cn.array [pos]; - if (cnAtPos instanceof INode) { - INode in = (INode) cnAtPos; - if (startgen == in.gen) - return in.rec_insertif (k, v, hc, cond, lev + 5, this, startgen, ct); - else { - if (GCAS (cn, cn.renewed (startgen, ct), ct)) { - // return rec_insertif (k, v, hc, cond, lev, - // parent, startgen, ct); - // tailrec - continue; - } else - return null; - } - } else if (cnAtPos instanceof SNode) { - SNode sn = (SNode) cnAtPos; - if (cond == null) { - if (sn.hc == hc && equal (sn.k, k, ct)) { - if (GCAS (cn, cn.updatedAt (pos, new SNode (k, v, hc), gen), ct)) - return Option.makeOption(sn.v); - else - return null; - } else { - CNode rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct); - MainNode nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen); - if (GCAS (cn, nn, ct)) - return Option.makeOption(); // None; - else - return null; - } - - } else if (cond == INode.KEY_ABSENT) { - if (sn.hc == hc && equal (sn.k, k, ct)) - return Option.makeOption(sn.v); - else { - CNode rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct); - MainNode nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen); - if (GCAS (cn, nn, ct)) - return Option.makeOption (); // None - else - return null; - } - } else if (cond == INode.KEY_PRESENT) { - if (sn.hc == hc && equal (sn.k, k, ct)) { - if (GCAS (cn, cn.updatedAt (pos, new SNode (k, v, hc), gen), ct)) - return Option.makeOption (sn.v); - else - return null; - - } else - return Option.makeOption ();// None; - } else { - if (sn.hc == hc && equal (sn.k, k, ct) && sn.v == cond) { - if (GCAS (cn, cn.updatedAt (pos, new SNode (k, v, hc), gen), ct)) - return Option.makeOption (sn.v); - else - return null; - } else - return Option.makeOption (); // None - } - - } - } else if (cond == null || cond == INode.KEY_ABSENT) { - CNode rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct); - CNode ncnode = rn.insertedAt (pos, flag, new SNode (k, v, hc), gen); - if (GCAS (cn, ncnode, ct)) - return Option.makeOption ();// None - else - return null; - } else if (cond == INode.KEY_PRESENT) { - return Option.makeOption ();// None; - } else - return Option.makeOption (); // None - } else if (m instanceof TNode) { - clean (parent, ct, lev - 5); - return null; - } else if (m instanceof LNode) { - // 3) an l-node - LNode ln = (LNode) m; - if (cond == null) { - Option optv = ln.get (k); - if (insertln (ln, k, v, ct)) - return optv; - else - return null; - } else if (cond == INode.KEY_ABSENT) { - Option t = ln.get (k); - if (t == null) { - if (insertln (ln, k, v, ct)) - return Option.makeOption ();// None - else - return null; - } else - return t; - } else if (cond == INode.KEY_PRESENT) { - Option t = ln.get (k); - if (t != null) { - if (insertln (ln, k, v, ct)) - return t; - else - return null; - } else - return null; // None - } else { - Option t = ln.get (k); - if (t != null) { - if (((Some) t).get () == cond) { - if (insertln (ln, k, v, ct)) - return new Some ((V) cond); - else - return null; - - } else - return Option.makeOption (); - } - } - } - -// throw new RuntimeException ("Should not happen"); - } - } - - final boolean insertln (final LNode ln, final K k, final V v, final TrieMap ct) { - LNode nn = ln.inserted (k, v); - return GCAS (ln, nn, ct); - } - - /** - * Looks up the value associated with the key. - * - * @return null if no value has been found, RESTART if the operation - * wasn't successful, or any other value otherwise - */ - final Object rec_lookup (final K k, final int hc, int lev, INode parent, final Gen startgen, final TrieMap ct) { - while (true) { - MainNode m = GCAS_READ (ct); // use -Yinline! - - if (m instanceof CNode) { - // 1) a multinode - final CNode cn = (CNode) m; - int idx = (hc >>> lev) & 0x1f; - int flag = 1 << idx; - int bmp = cn.bitmap; - if ((bmp & flag) == 0) - return null; // 1a) bitmap shows no binding - else { // 1b) bitmap contains a value - descend - int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount (bmp & (flag - 1)); - final BasicNode sub = cn.array [pos]; - if (sub instanceof INode) { - INode in = (INode) sub; - if (ct.isReadOnly () || (startgen == ((INodeBase) sub).gen)) - return in.rec_lookup (k, hc, lev + 5, this, startgen, ct); - else { - if (GCAS (cn, cn.renewed (startgen, ct), ct)) { - // return rec_lookup (k, hc, lev, parent, - // startgen, ct); - // Tailrec - continue; - } else - return RESTART; // used to be throw - // RestartException - } - } else if (sub instanceof SNode) { - // 2) singleton node - SNode sn = (SNode) sub; - if (((SNode) sub).hc == hc && equal (sn.k, k, ct)) - return sn.v; - else - return null; - } - } - } else if (m instanceof TNode) { - // 3) non-live node - return cleanReadOnly ((TNode) m, lev, parent, ct, k, hc); - } else if (m instanceof LNode) { - // 5) an l-node - Option tmp = ((LNode) m).get (k); - return (tmp instanceof Option) ? ((Option) tmp) : null; - } - - throw new RuntimeException ("Should not happen"); - } - } - - private Object cleanReadOnly (final TNode tn, final int lev, final INode parent, final TrieMap ct, K k, int hc) { - if (ct.nonReadOnly ()) { - clean (parent, ct, lev - 5); - return RESTART; // used to be throw RestartException - } else { - if (tn.hc == hc && tn.k == k) - return tn.v; - else - return null; - } - } - - /** - * Removes the key associated with the given value. - * - * @param v - * if null, will remove the key irregardless of the value; - * otherwise removes only if binding contains that exact key - * and value - * @return null if not successful, an Option[V] indicating the previous - * value otherwise - */ - final Option rec_remove (K k, V v, int hc, int lev, final INode parent, final Gen startgen, final TrieMap ct) { - MainNode m = GCAS_READ (ct); // use -Yinline! - - if (m instanceof CNode) { - CNode cn = (CNode) m; - int idx = (hc >>> lev) & 0x1f; - int bmp = cn.bitmap; - int flag = 1 << idx; - if ((bmp & flag) == 0) - return Option.makeOption (); - else { - int pos = Integer.bitCount (bmp & (flag - 1)); - BasicNode sub = cn.array [pos]; - Option res = null; - if (sub instanceof INode) { - INode in = (INode) sub; - if (startgen == in.gen) - res = in.rec_remove (k, v, hc, lev + 5, this, startgen, ct); - else { - if (GCAS (cn, cn.renewed (startgen, ct), ct)) - res = rec_remove (k, v, hc, lev, parent, startgen, ct); - else - res = null; - } - - } else if (sub instanceof SNode) { - SNode sn = (SNode) sub; - if (sn.hc == hc && equal (sn.k, k, ct) && (v == null || v.equals(sn.v))) { - MainNode ncn = cn.removedAt (pos, flag, gen).toContracted (lev); - if (GCAS (cn, ncn, ct)) - res = Option.makeOption (sn.v); - else - res = null; - } else - res = Option.makeOption (); - } - - if (res instanceof None || (res == null)) - return res; - else { - if (parent != null) { // never tomb at root - MainNode n = GCAS_READ (ct); - if (n instanceof TNode) - cleanParent (n, parent, ct, hc, lev, startgen); - } - - return res; - } - } - } else if (m instanceof TNode) { - clean (parent, ct, lev - 5); - return null; - } else if (m instanceof LNode) { - LNode ln = (LNode) m; - if (v == null) { - Option optv = ln.get (k); - MainNode nn = ln.removed (k, ct); - if (GCAS (ln, nn, ct)) - return optv; - else - return null; - } else { - Option tmp = ln.get (k); - if (tmp instanceof Some) { - Some tmp1 = (Some) tmp; - if (tmp1.get () == v) { - MainNode nn = ln.removed (k, ct); - if (GCAS (ln, nn, ct)) - return tmp; - else - return null; - } - } - } - } - throw new RuntimeException ("Should not happen"); - } - - void cleanParent (final Object nonlive, final INode parent, final TrieMap ct, final int hc, final int lev, final Gen startgen) { - while (true) { - MainNode pm = parent.GCAS_READ (ct); - if (pm instanceof CNode) { - CNode cn = (CNode) pm; - int idx = (hc >>> (lev - 5)) & 0x1f; - int bmp = cn.bitmap; - int flag = 1 << idx; - if ((bmp & flag) == 0) { - } // somebody already removed this i-node, we're done - else { - int pos = Integer.bitCount (bmp & (flag - 1)); - BasicNode sub = cn.array [pos]; - if (sub == this) { - if (nonlive instanceof TNode) { - TNode tn = (TNode) nonlive; - MainNode ncn = cn.updatedAt (pos, tn.copyUntombed (), gen).toContracted (lev - 5); - if (!parent.GCAS (cn, ncn, ct)) - if (ct.readRoot ().gen == startgen) { - // cleanParent (nonlive, parent, ct, hc, - // lev, startgen); - // tailrec - continue; - } - } - } - } - } else { - // parent is no longer a cnode, we're done - } - break; - } - } - - private void clean (final INode nd, final TrieMap ct, int lev) { - MainNode m = nd.GCAS_READ (ct); - if (m instanceof CNode) { - CNode cn = (CNode) m; - nd.GCAS (cn, cn.toCompressed (ct, lev, gen), ct); - } - } - - final boolean isNullInode (final TrieMap ct) { - return GCAS_READ (ct) == null; - } - - final int cachedSize (final TrieMap ct) { - MainNode m = GCAS_READ (ct); - return m.cachedSize (ct); - } - - // /* this is a quiescent method! */ - // def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode - // match { - // case null => "" - // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v, - // tn.hc) - // case cn: CNode[_, _] => cn.string(lev) - // case ln: LNode[_, _] => ln.string(lev) - // case x => "".format(x) - // }) - - public String string (int lev) { - return "INode"; - } - - } - - private static final class FailedNode extends MainNode { - MainNode p; - - FailedNode (final MainNode p) { - this.p = p; - WRITE_PREV (p); - } - - public String string (int lev) { - throw new UnsupportedOperationException (); - } - - public int cachedSize (Object ct) { - throw new UnsupportedOperationException (); - } - - public String toString () { - return String.format ("FailedNode(%s)", p); - } - } - - private interface KVNode { - Entry kvPair(); - } - - private static final class SNode extends BasicNode implements KVNode { - final K k; - final V v; - final int hc; - - SNode (final K k, final V v, final int hc) { - this.k = k; - this.v = v; - this.hc = hc; - } - - final SNode copy() { - return new SNode (k, v, hc); - } - - final TNode copyTombed () { - return new TNode (k, v, hc); - } - - final SNode copyUntombed () { - return new SNode (k, v, hc); - } - - final public Entry kvPair () { - return new Pair(k, v); - } - - final public String string (int lev) { - // (" " * lev) + "SNode(%s, %s, %x)".format(k, v, hc); - return "SNode"; - } - } - - private static final class TNode extends MainNode implements KVNode { - final K k; - final V v; - final int hc; - - TNode (final K k, final V v, final int hc) { - this.k = k; - this.v = v; - this.hc = hc; - } - - final TNode copy () { - return new TNode (k, v, hc); - } - - final TNode copyTombed () { - return new TNode (k, v, hc); - } - - final SNode copyUntombed () { - return new SNode (k, v, hc); - } - - final public Pair kvPair () { - return new Pair(k, v); - } - - final public int cachedSize (Object ct) { - return 1; - } - - final public String string (int lev) { - // (" " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc); - return "TNode"; - } - } - - private final static class LNode extends MainNode { - final ListMap listmap; - - public LNode (final ListMap listmap) { - this.listmap = listmap; - } - - public LNode(K k, V v) { - this (ListMap.map (k, v)); - } - - public LNode (K k1, V v1, K k2, V v2) { - this (ListMap.map (k1, v1, k2, v2)); - } - - LNode inserted (K k, V v) { - return new LNode (listmap.add (k, v)); - } - - MainNode removed (K k, final TrieMap ct) { - ListMap updmap = listmap.remove (k); - if (updmap.size () > 1) - return new LNode (updmap); - else { - Entry kv = updmap.iterator ().next (); - // create it tombed so that it gets compressed on subsequent - // accesses - return new TNode (kv.getKey (), kv.getValue (), ct.computeHash (kv.getKey ())); - } - } - - Option get (K k) { - return listmap.get (k); - } - - public int cachedSize (Object ct) { - return listmap.size (); - } - - public String string (int lev) { - // (" " * lev) + "LNode(%s)".format(listmap.mkString(", ")) - return "LNode"; - } - } - - private static final class CNode extends CNodeBase { - - final int bitmap; - final BasicNode[] array; - final Gen gen; - - CNode (final int bitmap, final BasicNode[] array, final Gen gen) { - this.bitmap = bitmap; - this.array = array; - this.gen = gen; - } - - // this should only be called from within read-only snapshots - final public int cachedSize (Object ct) { - int currsz = READ_SIZE (); - if (currsz != -1) - return currsz; - else { - int sz = computeSize ((TrieMap) ct); - while (READ_SIZE () == -1) - CAS_SIZE (-1, sz); - return READ_SIZE (); - } - } - - // lends itself towards being parallelizable by choosing - // a random starting offset in the array - // => if there are concurrent size computations, they start - // at different positions, so they are more likely to - // to be independent - private int computeSize (final TrieMap ct) { - int i = 0; - int sz = 0; - // final int offset = (array.length > 0) ? - // // util.Random.nextInt(array.length) /* <-- benchmarks show that - // // this causes observable contention */ - // scala.concurrent.forkjoin.ThreadLocalRandom.current.nextInt (0, - // array.length) - // : 0; - - final int offset = 0; - while (i < array.length) { - int pos = (i + offset) % array.length; - BasicNode elem = array [pos]; - if (elem instanceof SNode) - sz += 1; - else if (elem instanceof INode) - sz += ((INode) elem).cachedSize (ct); - i += 1; - } - return sz; - } - - final CNode updatedAt (int pos, final BasicNode nn, final Gen gen) { - int len = array.length; - BasicNode[] narr = new BasicNode[len]; - System.arraycopy (array, 0, narr, 0, len); - narr [pos] = nn; - return new CNode (bitmap, narr, gen); - } - - final CNode removedAt (int pos, int flag, final Gen gen) { - BasicNode[] arr = array; - int len = arr.length; - BasicNode[] narr = new BasicNode[len - 1]; - System.arraycopy (arr, 0, narr, 0, pos); - System.arraycopy (arr, pos + 1, narr, pos, len - pos - 1); - return new CNode (bitmap ^ flag, narr, gen); - } - - final CNode insertedAt (int pos, int flag, final BasicNode nn, final Gen gen) { - int len = array.length; - int bmp = bitmap; - BasicNode[] narr = new BasicNode[len + 1]; - System.arraycopy (array, 0, narr, 0, pos); - narr [pos] = nn; - System.arraycopy (array, pos, narr, pos + 1, len - pos); - return new CNode (bmp | flag, narr, gen); - } - - /** - * Returns a copy of this cnode such that all the i-nodes below it are - * copied to the specified generation `ngen`. - */ - final CNode renewed (final Gen ngen, final TrieMap ct) { - int i = 0; - BasicNode[] arr = array; - int len = arr.length; - BasicNode[] narr = new BasicNode[len]; - while (i < len) { - BasicNode elem = arr [i]; - if (elem instanceof INode) { - INode in = (INode) elem; - narr [i] = in.copyToGen (ngen, ct); - } else if (elem instanceof BasicNode) - narr [i] = elem; - i += 1; - } - return new CNode (bitmap, narr, ngen); - } - - private BasicNode resurrect (final INode inode, final Object inodemain) { - if (inodemain instanceof TNode) { - TNode tn = (TNode) inodemain; - return tn.copyUntombed (); - } else - return inode; - } - - final MainNode toContracted (int lev) { - if (array.length == 1 && lev > 0) { - if (array [0] instanceof SNode) { - SNode sn = (SNode) array [0]; - return sn.copyTombed (); - } else - return this; - - } else - return this; - } - - // - if the branching factor is 1 for this CNode, and the child - // is a tombed SNode, returns its tombed version - // - otherwise, if there is at least one non-null node below, - // returns the version of this node with at least some null-inodes - // removed (those existing when the op began) - // - if there are only null-i-nodes below, returns null - final MainNode toCompressed (final TrieMap ct, int lev, Gen gen) { - int bmp = bitmap; - int i = 0; - BasicNode[] arr = array; - BasicNode[] tmparray = new BasicNode[arr.length]; - while (i < arr.length) { // construct new bitmap - BasicNode sub = arr [i]; - if (sub instanceof INode) { - INode in = (INode) sub; - MainNode inodemain = in.gcasRead (ct); - assert (inodemain != null); - tmparray [i] = resurrect (in, inodemain); - } else if (sub instanceof SNode) { - tmparray [i] = sub; - } - i += 1; - } - - return new CNode (bmp, tmparray, gen).toContracted (lev); - } - - public String string (int lev) { - // "CNode %x\n%s".format(bitmap, array.map(_.string(lev + - // 1)).mkString("\n")); - return "CNode"; - } - - /* - * quiescently consistent - don't call concurrently to anything - * involving a GCAS!! - */ - // protected Seq collectElems() { - // array flatMap { - // case sn: SNode[K, V] => Some(sn.kvPair) - // case in: INode[K, V] => in.mainnode match { - // case tn: TNode[K, V] => Some(tn.kvPair) - // case ln: LNode[K, V] => ln.listmap.toList - // case cn: CNode[K, V] => cn.collectElems - // } - // } - // } - - // protected Seq collectLocalElems() { - // // array flatMap { - // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString) - // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen + - // ")") - // // } - // return null; - // } - - public String toString () { - // val elems = collectLocalElems - // "CNode(sz: %d; %s)".format(elems.size, - // elems.sorted.mkString(", ")) - return "CNode"; - } - - static MainNode dual (final SNode x, int xhc, final SNode y, int yhc, int lev, Gen gen) { - if (lev < 35) { - int xidx = (xhc >>> lev) & 0x1f; - int yidx = (yhc >>> lev) & 0x1f; - int bmp = (1 << xidx) | (1 << yidx); - - if (xidx == yidx) { - INode subinode = new INode (gen);// (TrieMap.inodeupdater) - subinode.mainnode = dual (x, xhc, y, yhc, lev + 5, gen); - return new CNode (bmp, new BasicNode[] { subinode }, gen); - } else { - if (xidx < yidx) - return new CNode (bmp, new BasicNode[] { x, y }, gen); - else - return new CNode (bmp, new BasicNode[] { y, x }, gen); - } - } else { - return new LNode (x.k, x.v, y.k, y.v); - } - } - - } - - private static class RDCSS_Descriptor { - INode old; - MainNode expectedmain; - INode nv; - volatile boolean committed = false; - - public RDCSS_Descriptor (final INode old, final MainNode expectedmain, final INode nv) { - this.old = old; - this.expectedmain = expectedmain; - this.nv = nv; - } - - } - - private static class Equiv implements Serializable { - private static final long serialVersionUID = 1L; - - public boolean equiv (K k1, K k2) { - return k1.equals (k2); - } - - static final Equiv universal = new Equiv (); - } - - private static interface Hashing extends Serializable { - public int hash(K k); - } - - static class Default implements Hashing { - private static final long serialVersionUID = 1L; - - public int hash (K k) { - int h = k.hashCode (); - // This function ensures that hashCodes that differ only by - // constant multiples at each bit position have a bounded - // number of collisions (approximately 8 at default load factor). - h ^= (h >>> 20) ^ (h >>> 12); - h ^= (h >>> 7) ^ (h >>> 4); - return h; - } - - static final Default instance = new Default (); - } - - private final Hashing hashingobj; - private final Equiv equalityobj; - - Hashing hashing () { - return hashingobj; - } - - Equiv equality () { - return equalityobj; - } - - private transient volatile Object root; - private final transient boolean readOnly; - - TrieMap (final Hashing hashf, final Equiv ef, final boolean readOnly) { - this.hashingobj = hashf; - this.equalityobj = ef; - this.readOnly = readOnly; - } - - TrieMap (final Object r, final Hashing hashf, final Equiv ef, boolean readOnly) { - this(hashf, ef, readOnly); - this.root = r; - } - - public TrieMap (final Hashing hashf, final Equiv ef) { - this(INode.newRootNode(), hashf, ef, false); - } - - public TrieMap () { - this (Default.instance, Equiv.universal); - } - - /* internal methods */ - - // private void writeObject(java.io.ObjectOutputStream out) { - // out.writeObject(hashf); - // out.writeObject(ef); - // - // Iterator it = iterator(); - // while (it.hasNext) { - // val (k, v) = it.next(); - // out.writeObject(k); - // out.writeObject(v); - // } - // out.writeObject(TrieMapSerializationEnd); - // } - // - // private TrieMap readObject(java.io.ObjectInputStream in) { - // root = INode.newRootNode(); - // rootupdater = AtomicReferenceFieldUpdater.newUpdater(TrieMap.class, - // Object.class, "root"); - // - // hashingobj = in.readObject(); - // equalityobj = in.readObject(); - // - // Object obj = null; - // do { - // obj = in.readObject(); - // if (obj != TrieMapSerializationEnd) { - // K k = (K)obj; - // V = (V)in.readObject(); - // update(k, v); - // } - // } while (obj != TrieMapSerializationEnd); - // } - - final boolean CAS_ROOT (Object ov, Object nv) { - if (isReadOnly()) { - throw new IllegalStateException("Attempted to modify a read-only snapshot"); - } - return ROOT_UPDATER.compareAndSet (this, ov, nv); - } - - // FIXME: abort = false by default - final INode readRoot (boolean abort) { - return RDCSS_READ_ROOT (abort); - } - - final INode readRoot () { - return RDCSS_READ_ROOT (false); - } - - final INode RDCSS_READ_ROOT () { - return RDCSS_READ_ROOT (false); - } - - final INode RDCSS_READ_ROOT (boolean abort) { - Object r = /* READ */root; - if (r instanceof INode) - return (INode) r; - else if (r instanceof RDCSS_Descriptor) { - return RDCSS_Complete (abort); - } - throw new RuntimeException ("Should not happen"); - } - - private final INode RDCSS_Complete (final boolean abort) { - while (true) { - Object v = /* READ */root; - if (v instanceof INode) - return (INode) v; - else if (v instanceof RDCSS_Descriptor) { - RDCSS_Descriptor desc = (RDCSS_Descriptor) v; - INode ov = desc.old; - MainNode exp = desc.expectedmain; - INode nv = desc.nv; - - if (abort) { - if (CAS_ROOT (desc, ov)) - return ov; - else { - // return RDCSS_Complete (abort); - // tailrec - continue; - } - } else { - MainNode oldmain = ov.gcasRead (this); - if (oldmain == exp) { - if (CAS_ROOT (desc, nv)) { - desc.committed = true; - return nv; - } else { - // return RDCSS_Complete (abort); - // tailrec - continue; - } - } else { - if (CAS_ROOT (desc, ov)) - return ov; - else { - // return RDCSS_Complete (abort); - // tailrec - continue; - - } - } - } - } - - throw new RuntimeException ("Should not happen"); - } - } - - private boolean RDCSS_ROOT (final INode ov, final MainNode expectedmain, final INode nv) { - RDCSS_Descriptor desc = new RDCSS_Descriptor (ov, expectedmain, nv); - if (CAS_ROOT (ov, desc)) { - RDCSS_Complete (false); - return /* READ */desc.committed; - } else - return false; - } - - private void inserthc (final K k, final int hc, final V v) { - while (true) { - INode r = RDCSS_READ_ROOT (); - if (!r.rec_insert (k, v, hc, 0, null, r.gen, this)) { - // inserthc (k, hc, v); - // tailrec - continue; - } - break; - } - } - - private Option insertifhc (final K k, final int hc, final V v, final Object cond) { - while (true) { - INode r = RDCSS_READ_ROOT (); - - Option ret = r.rec_insertif (k, v, hc, cond, 0, null, r.gen, this); - if (ret == null) { - // return insertifhc (k, hc, v, cond); - // tailrec - continue; - } else - return ret; - } - } - - private Object lookuphc (final K k, final int hc) { - while (true) { - INode r = RDCSS_READ_ROOT (); - Object res = r.rec_lookup (k, hc, 0, null, r.gen, this); - if (res == INodeBase.RESTART) { - // return lookuphc (k, hc); - // tailrec - continue; - } else - return res; - } - } - - private Option removehc (final K k, final V v, final int hc) { - while (true) { - INode r = RDCSS_READ_ROOT (); - Option res = r.rec_remove (k, v, hc, 0, null, r.gen, this); - if (res != null) - return res; - else { - // return removehc (k, v, hc); - // tailrec - continue; - } - } - } - - /** - * Ensure this instance is read-write, throw UnsupportedOperationException - * otherwise. Used by Map-type methods for quick check. - */ - private void ensureReadWrite() { - if (isReadOnly()) { - throw new UnsupportedOperationException("Attempted to modify a read-only view"); - } - } - - public String string () { - // RDCSS_READ_ROOT().string(0); - return "Root"; - } - - /* public methods */ - - // public Seq seq() { - // return this; - // } - - // override def par = new ParTrieMap(this) - - // static TrieMap empty() { - // return new TrieMap(); - // } - - final boolean isReadOnly () { - return readOnly; - } - - final boolean nonReadOnly () { - return !readOnly; - } - - /** - * Returns a snapshot of this TrieMap. This operation is lock-free and - * linearizable. - * - * The snapshot is lazily updated - the first time some branch in the - * snapshot or this TrieMap are accessed, they are rewritten. This means - * that the work of rebuilding both the snapshot and this TrieMap is - * distributed across all the threads doing updates or accesses subsequent - * to the snapshot creation. - */ - - final public TrieMap snapshot () { - while (true) { - INode r = RDCSS_READ_ROOT (); - final MainNode expmain = r.gcasRead (this); - if (RDCSS_ROOT (r, expmain, r.copyToGen (new Gen(), this))) - return new TrieMap (r.copyToGen (new Gen(), this), hashing (), equality (), readOnly); - else { - // return snapshot (); - // tailrec - continue; - } - } - } - - /** - * Returns a read-only snapshot of this TrieMap. This operation is lock-free - * and linearizable. - * - * The snapshot is lazily updated - the first time some branch of this - * TrieMap are accessed, it is rewritten. The work of creating the snapshot - * is thus distributed across subsequent updates and accesses on this - * TrieMap by all threads. Note that the snapshot itself is never rewritten - * unlike when calling the `snapshot` method, but the obtained snapshot - * cannot be modified. - * - * This method is used by other methods such as `size` and `iterator`. - */ - final public TrieMap readOnlySnapshot () { - // Is it a snapshot of a read-only snapshot? - if(!nonReadOnly ()) - return this; - - while (true) { - INode r = RDCSS_READ_ROOT (); - MainNode expmain = r.gcasRead (this); - if (RDCSS_ROOT (r, expmain, r.copyToGen (new Gen(), this))) - return new TrieMap (r, hashing (), equality (), true); - else { - // return readOnlySnapshot (); - continue; - } - } - } - - final public void clear () { - while (true) { - INode r = RDCSS_READ_ROOT (); - if (!RDCSS_ROOT (r, r.gcasRead (this), INode.newRootNode ())) { - continue; - }else{ - return; - } - } - } - - // @inline - int computeHash (K k) { - return hashingobj.hash (k); - } - - final V lookup (K k) { - int hc = computeHash (k); -// return (V) lookuphc (k, hc); - Object o = lookuphc (k, hc); - if(o instanceof Some) { - return ((Some)o).get (); - } else if(o instanceof None) - return null; - else - return (V)o; - } - - final V apply (K k) { - int hc = computeHash (k); - Object res = lookuphc (k, hc); - if (res == null) - throw new NoSuchElementException (); - else - return (V) res; - } - -// final public Option get (K k) { -// int hc = computeHash (k); -// return Option.makeOption ((V)lookuphc (k, hc)); -// } - - final public V get (Object k) { - return lookup((K)k); - } - - final public Option putOpt(Object key, Object value) { - int hc = computeHash ((K)key); - return insertifhc ((K)key, hc, (V)value, null); - } - - @Override - final public V put (Object key, Object value) { - ensureReadWrite(); - int hc = computeHash ((K)key); - Option ov = insertifhc ((K)key, hc, (V)value, null); - if(ov instanceof Some) { - Some sv = (Some)ov; - return sv.get (); - } else - return null; - } - - final public void update (K k, V v) { - int hc = computeHash (k); - inserthc (k, hc, v); - } - - final public TrieMap add (K k, V v) { - update (k, v); - return this; - } - - final Option removeOpt (K k) { - int hc = computeHash (k); - return removehc (k, (V) null, hc); - } - - @Override - final public V remove (Object k) { - ensureReadWrite(); - int hc = computeHash ((K)k); - Option ov = removehc ((K)k, (V) null, hc); - if(ov instanceof Some) { - Some sv = (Some)ov; - return sv.get(); - } else - return null; - } - -// final public TrieMap remove (Object k) { -// removeOpt ((K)k); -// return this; -// } - - final public Option putIfAbsentOpt (K k, V v) { - int hc = computeHash (k); - return insertifhc (k, hc, v, INode.KEY_ABSENT); - } - - @Override - final public V putIfAbsent (Object k, Object v) { - ensureReadWrite(); - int hc = computeHash ((K)k); - Option ov = insertifhc ((K)k, hc, (V)v, INode.KEY_ABSENT); - if(ov instanceof Some) { - Some sv = (Some)ov; - return sv.get(); - } else - return null; - } - - @Override - public boolean remove (Object k, Object v) { - ensureReadWrite(); - int hc = computeHash ((K)k); - return removehc ((K)k, (V)v, hc).nonEmpty (); - } - - @Override - public boolean replace (K k, V oldvalue, V newvalue) { - ensureReadWrite(); - int hc = computeHash (k); - return insertifhc (k, hc, newvalue, (Object) oldvalue).nonEmpty (); - } - - public Option replaceOpt (K k, V v) { - int hc = computeHash (k); - return insertifhc (k, hc, v, INode.KEY_PRESENT); - } - - @Override - public V replace (Object k, Object v) { - ensureReadWrite(); - int hc = computeHash ((K)k); - Option ov = insertifhc ((K)k, hc, (V)v, INode.KEY_PRESENT); - if(ov instanceof Some) { - Some sv = (Some)ov; - return sv.get(); - } else - return null; - } - - /*** - * Return an iterator over a TrieMap. - * - * If this is a read-only snapshot, it would return a read-only iterator. - * - * If it is the original TrieMap or a non-readonly snapshot, it would return - * an iterator that would allow for updates. - * - * @return - */ - public Iterator> iterator () { - if (!nonReadOnly ()) - return readOnlySnapshot ().readOnlyIterator (); - else - return new TrieMapIterator (0, this); - } - - /*** - * Return an iterator over a TrieMap. - * This is a read-only iterator. - * - * @return - */ - public Iterator> readOnlyIterator () { - if (nonReadOnly ()) - return readOnlySnapshot ().readOnlyIterator (); - else - return new TrieMapReadOnlyIterator (0, this); - } - - private int cachedSize () { - INode r = RDCSS_READ_ROOT (); - return r.cachedSize (this); - } - - public int size () { - if (nonReadOnly ()) - return readOnlySnapshot ().size (); - else - return cachedSize (); - } - - String stringPrefix () { - return "TrieMap"; - } - - /*** - * This iterator is a read-only one and does not allow for any update - * operations on the underlying data structure. - * - * @param - * @param - */ - private static class TrieMapReadOnlyIterator extends TrieMapIterator { - TrieMapReadOnlyIterator (int level, final TrieMap ct, boolean mustInit) { - super (level, ct, mustInit); - } - - TrieMapReadOnlyIterator (int level, TrieMap ct) { - this (level, ct, true); - } - void initialize () { - assert (ct.isReadOnly ()); - super.initialize (); - } - - public void remove () { - throw new UnsupportedOperationException ("Operation not supported for read-only iterators"); - } - - Entry nextEntry(final Entry rr) { - // Return non-updatable entry - return rr; - } - } - - private static class TrieMapIterator implements Iterator> { - private int level; - protected TrieMap ct; - private final boolean mustInit; - private BasicNode[][] stack = new BasicNode[7][]; - private int[] stackpos = new int[7]; - private int depth = -1; - private Iterator> subiter = null; - private KVNode current = null; - private Entry lastReturned = null; - - TrieMapIterator (int level, final TrieMap ct, boolean mustInit) { - this.level = level; - this.ct = ct; - this.mustInit = mustInit; - if (this.mustInit) - initialize (); - } - - TrieMapIterator (int level, TrieMap ct) { - this (level, ct, true); - } - - - public boolean hasNext () { - return (current != null) || (subiter != null); - } - - public Entry next () { - if (hasNext ()) { - Entry r = null; - if (subiter != null) { - r = subiter.next (); - checkSubiter (); - } else { - r = current.kvPair (); - advance (); - } - - lastReturned = r; - if(r instanceof Entry) { - final Entry rr = (Entry)r; - return nextEntry(rr); - } - return r; - } else { - // return Iterator.empty ().next (); - return null; - } - } - - Entry nextEntry(final Entry rr) { - return new Entry() { - private V updated = null; - - @Override - public K getKey () { - return rr.getKey (); - } - - @Override - public V getValue () { - return (updated == null)?rr.getValue (): updated; - } - - @Override - public V setValue (V value) { - updated = value; - return ct.replace (getKey (), value); - } - }; - } - - private void readin (INode in) { - MainNode m = in.gcasRead (ct); - if (m instanceof CNode) { - CNode cn = (CNode) m; - depth += 1; - stack [depth] = cn.array; - stackpos [depth] = -1; - advance (); - } else if (m instanceof TNode) { - current = (TNode) m; - } else if (m instanceof LNode) { - subiter = ((LNode) m).listmap.iterator (); - checkSubiter (); - } else if (m == null) { - current = null; - } - } - - // @inline - private void checkSubiter () { - if (!subiter.hasNext ()) { - subiter = null; - advance (); - } - } - - // @inline - void initialize () { -// assert (ct.isReadOnly ()); - INode r = ct.RDCSS_READ_ROOT (); - readin (r); - } - - void advance () { - if (depth >= 0) { - int npos = stackpos [depth] + 1; - if (npos < stack [depth].length) { - stackpos [depth] = npos; - BasicNode elem = stack [depth] [npos]; - if (elem instanceof SNode) { - current = (SNode) elem; - } else if (elem instanceof INode) { - readin ((INode) elem); - } - } else { - depth -= 1; - advance (); - } - } else - current = null; - } - - protected TrieMapIterator newIterator (int _lev, TrieMap _ct, boolean _mustInit) { - return new TrieMapIterator (_lev, _ct, _mustInit); - } - - protected void dupTo (TrieMapIterator it) { - it.level = this.level; - it.ct = this.ct; - it.depth = this.depth; - it.current = this.current; - - // these need a deep copy - System.arraycopy (this.stack, 0, it.stack, 0, 7); - System.arraycopy (this.stackpos, 0, it.stackpos, 0, 7); - - // this one needs to be evaluated - if (this.subiter == null) - it.subiter = null; - else { - List> lst = toList (this.subiter); - this.subiter = lst.iterator (); - it.subiter = lst.iterator (); - } - } - - // /** Returns a sequence of iterators over subsets of this iterator. - // * It's used to ease the implementation of splitters for a parallel - // version of the TrieMap. - // */ - // protected def subdivide(): Seq[Iterator[(K, V)]] = if (subiter ne - // null) { - // // the case where an LNode is being iterated - // val it = subiter - // subiter = null - // advance() - // this.level += 1 - // Seq(it, this) - // } else if (depth == -1) { - // this.level += 1 - // Seq(this) - // } else { - // var d = 0 - // while (d <= depth) { - // val rem = stack(d).length - 1 - stackpos(d) - // if (rem > 0) { - // val (arr1, arr2) = stack(d).drop(stackpos(d) + 1).splitAt(rem / 2) - // stack(d) = arr1 - // stackpos(d) = -1 - // val it = newIterator(level + 1, ct, false) - // it.stack(0) = arr2 - // it.stackpos(0) = -1 - // it.depth = 0 - // it.advance() // <-- fix it - // this.level += 1 - // return Seq(this, it) - // } - // d += 1 - // } - // this.level += 1 - // Seq(this) - // } - - private List> toList (Iterator> it) { - ArrayList> list = new ArrayList> (); - while (it.hasNext ()) { - list.add (it.next ()); - } - return list; - } - - void printDebug () { - System.out.println ("ctrie iterator"); - System.out.println (Arrays.toString (stackpos)); - System.out.println ("depth: " + depth); - System.out.println ("curr.: " + current); - // System.out.println(stack.mkString("\n")); - } - - @Override - public void remove () { - if (lastReturned != null) { - ct.remove (lastReturned.getKey ()); - lastReturned = null; - } else - throw new IllegalStateException(); - } - - } - - /** Only used for ctrie serialization. */ - // @SerialVersionUID(0L - 7237891413820527142L) - private static long TrieMapSerializationEnd = 0L - 7237891413820527142L; - - - public boolean containsKey (Object key) { - return lookup ((K) key) != null; - } - - - @Override - public Set> entrySet () { - return entrySet; - } - - /*** - * Support for EntrySet operations required by the Map interface - * - */ - final class EntrySet extends AbstractSet> { - - @Override - public Iterator> iterator () { - return TrieMap.this.iterator (); - } - - @Override - public final boolean contains (final Object o) { - if (!(o instanceof Entry)) { - return false; - } - final Entry e = (Entry) o; - final K k = e.getKey (); - final V v = lookup (k); - return v != null; - } - - @Override - public final boolean remove (final Object o) { - if (!(o instanceof Entry)) { - return false; - } - final Entry e = (Entry) o; - final K k = e.getKey (); - return null != TrieMap.this.remove (k); - } - - @Override - public final int size () { - int size = 0; - for (final Iterator i = iterator (); i.hasNext (); i.next ()) { - size++; - } - return size; - } - - @Override - public final void clear () { - TrieMap.this.clear (); - } - } - - private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { - inputStream.defaultReadObject(); - this.root = INode.newRootNode(); - - final boolean ro = inputStream.readBoolean(); - final int size = inputStream.readInt(); - for (int i = 0; i < size; ++i) { - final K key = (K)inputStream.readObject(); - final V value = (V)inputStream.readObject(); - add(key, value); - } - - // Propagate the read-only bit - try { - READONLY_FIELD.setBoolean(this, ro); - } catch (IllegalAccessException e) { - throw new IOException("Failed to set read-only flag", e); - } - } - - private void writeObject(ObjectOutputStream outputStream) throws IOException { - outputStream.defaultWriteObject(); - - final Map ro = readOnlySnapshot(); - outputStream.writeBoolean(isReadOnly()); - outputStream.writeInt(ro.size()); - - for (Entry e : ro.entrySet()) { - outputStream.writeObject(e.getKey()); - outputStream.writeObject(e.getValue()); - } - } -} diff --git a/libtest/src/main/java/tests/custom/counter/Counter.java b/libtest/src/main/java/tests/custom/counter/Counter.java deleted file mode 100644 index 10d33d600..000000000 --- a/libtest/src/main/java/tests/custom/counter/Counter.java +++ /dev/null @@ -1,27 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public interface Counter { - public int incrementAndGet(); -} \ No newline at end of file diff --git a/libtest/src/main/java/tests/custom/counter/CounterCorrect2.java b/libtest/src/main/java/tests/custom/counter/CounterCorrect2.java deleted file mode 100644 index acccbff7e..000000000 --- a/libtest/src/main/java/tests/custom/counter/CounterCorrect2.java +++ /dev/null @@ -1,38 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicInteger; - -public class CounterCorrect2 implements Counter { - private AtomicInteger c; - - public CounterCorrect2() { - c = new AtomicInteger(); - } - - @Override - public synchronized int incrementAndGet() { - return c.incrementAndGet(); - } -} diff --git a/libtest/src/main/java/tests/custom/counter/CounterGet.java b/libtest/src/main/java/tests/custom/counter/CounterGet.java deleted file mode 100644 index c1a57df9a..000000000 --- a/libtest/src/main/java/tests/custom/counter/CounterGet.java +++ /dev/null @@ -1,39 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterGet{ - private int c; - - public CounterGet() { - c = 0; - } - - public int get() { - return c; - } - - public synchronized int incrementAndGet() { - return ++c; - } -} diff --git a/libtest/src/main/java/tests/custom/counter/CounterWrong0.java b/libtest/src/main/java/tests/custom/counter/CounterWrong0.java deleted file mode 100644 index b734ff5d8..000000000 --- a/libtest/src/main/java/tests/custom/counter/CounterWrong0.java +++ /dev/null @@ -1,38 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterWrong0 implements Counter { - private int c; - - public CounterWrong0() { - c = 0; - } - - @Override - public int incrementAndGet() { -// c++; -// return c; - return ++c; - } -} diff --git a/libtest/src/main/java/tests/custom/counter/CounterWrong1.java b/libtest/src/main/java/tests/custom/counter/CounterWrong1.java deleted file mode 100644 index f279b3abb..000000000 --- a/libtest/src/main/java/tests/custom/counter/CounterWrong1.java +++ /dev/null @@ -1,37 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterWrong1 implements Counter { - private int c; - - public CounterWrong1() { - c = 0; - } - - @Override - public int incrementAndGet() { - c++; - return c; - } -} diff --git a/libtest/src/main/java/tests/custom/counter/CounterWrong2.java b/libtest/src/main/java/tests/custom/counter/CounterWrong2.java deleted file mode 100644 index 4b91b3c32..000000000 --- a/libtest/src/main/java/tests/custom/counter/CounterWrong2.java +++ /dev/null @@ -1,36 +0,0 @@ -package tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterWrong2 implements Counter { - private int c; - - public CounterWrong2() { - c = 0; - } - - @Override - public int incrementAndGet() { - return ++c; - } -} diff --git a/libtest/src/main/java/tests/custom/queue/Queue.java b/libtest/src/main/java/tests/custom/queue/Queue.java deleted file mode 100644 index 19b53810a..000000000 --- a/libtest/src/main/java/tests/custom/queue/Queue.java +++ /dev/null @@ -1,28 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public interface Queue { - public void put(int x) throws QueueFullException; - public int get() throws QueueEmptyException; -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueEmptyException.java b/libtest/src/main/java/tests/custom/queue/QueueEmptyException.java deleted file mode 100644 index 4510bf2ba..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueEmptyException.java +++ /dev/null @@ -1,33 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class QueueEmptyException extends Exception { - public QueueEmptyException(String message) { - super(message); - } - - public QueueEmptyException() { - - } -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueFullException.java b/libtest/src/main/java/tests/custom/queue/QueueFullException.java deleted file mode 100644 index 019561020..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueFullException.java +++ /dev/null @@ -1,33 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class QueueFullException extends Exception { - public QueueFullException(String message) { - super(message); - } - - public QueueFullException() { - - } -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueSynchronized.java b/libtest/src/main/java/tests/custom/queue/QueueSynchronized.java deleted file mode 100644 index 7eb34acbb..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueSynchronized.java +++ /dev/null @@ -1,77 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Arrays; - -public class QueueSynchronized implements Queue { - private int indGet; - private int indPut; - private int countElements; - - private int[] items; - - private int inc(int i) { - return (++i == items.length ? 0 : i); - } - - public QueueSynchronized(int capacity) { - items = new int[capacity]; - - indPut = 0; - indGet = 0; - countElements = 0; - - } - - @Override - public synchronized void put(int x) throws QueueFullException { - if (countElements == items.length) { - throw new QueueFullException(); - } - items[indPut] = x; - indPut = inc(indPut); - countElements++; - } - - @Override - public synchronized int get() throws QueueEmptyException { - if (countElements == 0) { - throw new QueueEmptyException(); - } - int ret = items[indGet]; - indGet = inc(indGet); - countElements--; - return ret; - } - - @Override - public String toString() { - return "QueueWithoutAnySync{" + - "indGet=" + indGet + - ", indPut=" + indPut + - ", countElements=" + countElements + - ", items=" + Arrays.toString(items) + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueWrong1.java b/libtest/src/main/java/tests/custom/queue/QueueWrong1.java deleted file mode 100644 index f545f67c2..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueWrong1.java +++ /dev/null @@ -1,77 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Arrays; - -public class QueueWrong1 implements Queue { - private int indGet; - private int indPut; - private int countElements; - - private int[] items; - - private int inc(int i) { - return (++i == items.length ? 0 : i); - } - - public QueueWrong1(int capacity) { - items = new int[capacity]; - - indPut = 0; - indGet = 0; - countElements = 0; - - } - - @Override - public void put(int x) throws QueueFullException { - if (countElements == items.length) { - throw new QueueFullException(); - } - items[indPut] = x; - indPut = inc(indPut); - countElements++; - } - - @Override - public int get() throws QueueEmptyException { - if (countElements == 0) { - throw new QueueEmptyException(); - } - int ret = items[indGet]; - indGet = inc(indGet); - countElements--; - return ret; - } - - @Override - public String toString() { - return "QueueWithoutAnySync{" + - "indGet=" + indGet + - ", indPut=" + indPut + - ", countElements=" + countElements + - ", items=" + Arrays.toString(items) + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueWrong2.java b/libtest/src/main/java/tests/custom/queue/QueueWrong2.java deleted file mode 100644 index 030e40103..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueWrong2.java +++ /dev/null @@ -1,77 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Arrays; - -public class QueueWrong2 implements Queue { - private int indGet; - private int indPut; - private int countElements; - - private int[] items; - - private int inc(int i) { - return (++i == items.length ? 0 : i); - } - - public QueueWrong2(int capacity) { - items = new int[capacity]; - - indPut = 0; - indGet = 0; - countElements = 0; - - } - - @Override - public synchronized void put(int x) throws QueueFullException { - if (countElements == items.length) { - throw new QueueFullException(); - } - items[indPut] = x; - indPut = inc(indPut); - countElements++; - } - - @Override - public int get() throws QueueEmptyException { - if (countElements == 0) { - throw new QueueEmptyException(); - } - int ret = items[indGet]; - indGet = inc(indGet); - countElements--; - return ret; - } - - @Override - public String toString() { - return "QueueWithoutAnySync{" + - "indGet=" + indGet + - ", indPut=" + indPut + - ", countElements=" + countElements + - ", items=" + Arrays.toString(items) + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/queue/QueueWrong3.java b/libtest/src/main/java/tests/custom/queue/QueueWrong3.java deleted file mode 100644 index 5df308cb7..000000000 --- a/libtest/src/main/java/tests/custom/queue/QueueWrong3.java +++ /dev/null @@ -1,77 +0,0 @@ -package tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Arrays; - -public class QueueWrong3 implements Queue { - private int indGet; - private int indPut; - private int countElements; - - private int[] items; - - private int inc(int i) { - return (++i == items.length ? 0 : i); - } - - public QueueWrong3(int capacity) { - items = new int[capacity]; - - indPut = 0; - indGet = 0; - countElements = 0; - - } - - @Override - public void put(int x) throws QueueFullException { - if (countElements == items.length) { - throw new QueueFullException(); - } - items[indPut] = x; - indPut = inc(indPut); - countElements++; - } - - @Override - public synchronized int get() throws QueueEmptyException { - if (countElements == 0) { - throw new QueueEmptyException(); - } - int ret = items[indGet]; - indGet = inc(indGet); - countElements--; - return ret; - } - - @Override - public String toString() { - return "QueueWithoutAnySync{" + - "indGet=" + indGet + - ", indPut=" + indPut + - ", countElements=" + countElements + - ", items=" + Arrays.toString(items) + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/transfer/Accounts.java b/libtest/src/main/java/tests/custom/transfer/Accounts.java deleted file mode 100644 index 73f118691..000000000 --- a/libtest/src/main/java/tests/custom/transfer/Accounts.java +++ /dev/null @@ -1,29 +0,0 @@ -package tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public interface Accounts { - public Integer getAmount(int id); - public void setAmount(int id, int value); - public void transfer(int id1, int id2, int value); -} diff --git a/libtest/src/main/java/tests/custom/transfer/AccountsWrong1.java b/libtest/src/main/java/tests/custom/transfer/AccountsWrong1.java deleted file mode 100644 index 66bc5be74..000000000 --- a/libtest/src/main/java/tests/custom/transfer/AccountsWrong1.java +++ /dev/null @@ -1,74 +0,0 @@ -package tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.HashMap; -import java.util.Map; - -public class AccountsWrong1 implements Accounts { - - Map data; - - public AccountsWrong1() { - data = new HashMap<>(); - } - - @Override - public Integer getAmount(int id) { - if (data.containsKey(id)) { - return data.get(id); - } else { - return 0; - } - } - - @Override - public void setAmount(int id, int value) { - data.put(id, value); - } - - @Override - public synchronized void transfer(int id1, int id2, int value) { - if (id1 == id2) return; - Integer v1 = data.get(id1); - Integer v2 = data.get(id2); - if (v1 == null) v1 = 0; - if (v2 == null) v2 = 0; - v1 -= value; - v2 += value; - data.put(id1, v1); -// try { -// Thread.sleep(10); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - data.put(id2, v2); - } - - @Override - public String toString() { - return "AccountsSynchronized{" + - "data=" + data + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/transfer/AccountsWrong2.java b/libtest/src/main/java/tests/custom/transfer/AccountsWrong2.java deleted file mode 100644 index 7479f8357..000000000 --- a/libtest/src/main/java/tests/custom/transfer/AccountsWrong2.java +++ /dev/null @@ -1,74 +0,0 @@ -package tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.HashMap; -import java.util.Map; - -public class AccountsWrong2 implements Accounts { - - Map data; - - public AccountsWrong2() { - data = new HashMap<>(); - } - - @Override - public Integer getAmount(int id) { - if (data.containsKey(id)) { - return data.get(id); - } else { - return 0; - } - } - - @Override - public synchronized void setAmount(int id, int value) { - data.put(id, value); - } - - @Override - public void transfer(int id1, int id2, int value) { - if (id1 == id2) return; - Integer v1 = data.get(id1); - Integer v2 = data.get(id2); - if (v1 == null) v1 = 0; - if (v2 == null) v2 = 0; - v1 -= value; - v2 += value; - data.put(id1, v1); -// try { -// Thread.sleep(10); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - data.put(id2, v2); - } - - @Override - public String toString() { - return "AccountsSynchronized{" + - "data=" + data + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/transfer/AccountsWrong3.java b/libtest/src/main/java/tests/custom/transfer/AccountsWrong3.java deleted file mode 100644 index 52a09685a..000000000 --- a/libtest/src/main/java/tests/custom/transfer/AccountsWrong3.java +++ /dev/null @@ -1,74 +0,0 @@ -package tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.HashMap; -import java.util.Map; - -public class AccountsWrong3 implements Accounts { - - Map data; - - public AccountsWrong3() { - data = new HashMap<>(); - } - - @Override - public Integer getAmount(int id) { - if (data.containsKey(id)) { - return data.get(id); - } else { - return 0; - } - } - - @Override - public void setAmount(int id, int value) { - data.put(id, value); - } - - @Override - public void transfer(int id1, int id2, int value) { - if (id1 == id2) return; - Integer v1 = data.get(id1); - Integer v2 = data.get(id2); - if (v1 == null) v1 = 0; - if (v2 == null) v2 = 0; - v1 -= value; - v2 += value; - data.put(id1, v1); -// try { -// Thread.sleep(10); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - data.put(id2, v2); - } - - @Override - public String toString() { - return "AccountsSynchronized{" + - "data=" + data + - '}'; - } -} diff --git a/libtest/src/main/java/tests/custom/transfer/AccountsWrong4.java b/libtest/src/main/java/tests/custom/transfer/AccountsWrong4.java deleted file mode 100644 index 02ee9ac98..000000000 --- a/libtest/src/main/java/tests/custom/transfer/AccountsWrong4.java +++ /dev/null @@ -1,66 +0,0 @@ -package tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.HashMap; -import java.util.Map; - -public class AccountsWrong4 implements Accounts { - - Map data; - - public AccountsWrong4() { - data = new HashMap<>(1); - } - - // This operation is not synchronized with others - @Override - public Integer getAmount(int id) { - return data.getOrDefault(id, 0); - } - - @Override - public synchronized void setAmount(int id, int value) { - data.put(id, value); - } - - @Override - public synchronized void transfer(int id1, int id2, int value) { - if (id1 == id2) return; - Integer v1 = data.get(id1); - Integer v2 = data.get(id2); - if (v1 == null) v1 = 0; - if (v2 == null) v2 = 0; - v1 -= value; - v2 += value; - data.put(id1, v1); - data.put(id2, v2); - } - - @Override - public String toString() { - return "AccountsSynchronized{" + - "data=" + data + - '}'; - } -} diff --git a/libtest/src/main/java/thesis_example/Counter.java b/libtest/src/main/java/thesis_example/Counter.java deleted file mode 100644 index e1315ba40..000000000 --- a/libtest/src/main/java/thesis_example/Counter.java +++ /dev/null @@ -1,27 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public interface Counter { - public int incrementAndGet(); -} \ No newline at end of file diff --git a/libtest/src/main/java/thesis_example/CounterCorrect1.java b/libtest/src/main/java/thesis_example/CounterCorrect1.java deleted file mode 100644 index 58f326e7b..000000000 --- a/libtest/src/main/java/thesis_example/CounterCorrect1.java +++ /dev/null @@ -1,36 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterCorrect1 { - private int c = 0; - - public synchronized int incrementAndGet() { - c++; - return c; - } - - public static void main(String[] args) { - System.out.println(new CounterCorrect1().incrementAndGet()); - } -} diff --git a/libtest/src/main/java/thesis_example/CounterCorrect2.java b/libtest/src/main/java/thesis_example/CounterCorrect2.java deleted file mode 100644 index c0d043a17..000000000 --- a/libtest/src/main/java/thesis_example/CounterCorrect2.java +++ /dev/null @@ -1,39 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicInteger; - - -public class CounterCorrect2 implements Counter { - private AtomicInteger c; - - public CounterCorrect2() { - c = new AtomicInteger(); - } - - @Override - public synchronized int incrementAndGet() { - return c.incrementAndGet(); - } -} diff --git a/libtest/src/main/java/thesis_example/CounterWrong1.java b/libtest/src/main/java/thesis_example/CounterWrong1.java deleted file mode 100644 index c10961c76..000000000 --- a/libtest/src/main/java/thesis_example/CounterWrong1.java +++ /dev/null @@ -1,37 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterWrong1 implements Counter { - private int c; - - public CounterWrong1() { - c = 0; - } - - @Override - public int incrementAndGet() { - c++; - return c; - } -} diff --git a/libtest/src/main/java/thesis_example/CounterWrong2.java b/libtest/src/main/java/thesis_example/CounterWrong2.java deleted file mode 100644 index af3175375..000000000 --- a/libtest/src/main/java/thesis_example/CounterWrong2.java +++ /dev/null @@ -1,36 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class CounterWrong2 implements Counter { - private int c; - - public CounterWrong2() { - c = 0; - } - - @Override - public int incrementAndGet() { - return ++c; - } -} diff --git a/libtest/src/main/java/thesis_example/QueueCorrect.java b/libtest/src/main/java/thesis_example/QueueCorrect.java deleted file mode 100644 index 09f27d0d1..000000000 --- a/libtest/src/main/java/thesis_example/QueueCorrect.java +++ /dev/null @@ -1,66 +0,0 @@ -package thesis_example; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import tests.custom.queue.QueueEmptyException; -import tests.custom.queue.QueueFullException; - -public class QueueCorrect { - private int indGet; - private int indPut; - private int countElements; - - private int[] items; - - private int inc(int i) { - return (++i == items.length ? 0 : i); - } - - public QueueCorrect(int capacity) { - items = new int[capacity]; - - indPut = 0; - indGet = 0; - countElements = 0; - - } - - public synchronized void put(int x) throws QueueFullException { - if (countElements == items.length) { - throw new QueueFullException(); - } - items[indPut] = x; - indPut = inc(indPut); - countElements++; - } - - public synchronized int get() throws QueueEmptyException { - if (countElements == 0) { - throw new QueueEmptyException(); - } - int ret = items[indGet]; - indGet = inc(indGet); - countElements--; - return ret; - } -} diff --git a/libtest/src/main/java/z/annotation/InternalUse.java b/libtest/src/main/java/z/annotation/InternalUse.java deleted file mode 100644 index 6f7e5eaff..000000000 --- a/libtest/src/main/java/z/annotation/InternalUse.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.annotation; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Documented -@Retention(RetentionPolicy.SOURCE) -public @interface InternalUse { - String value() default ""; -} diff --git a/libtest/src/main/java/z/annotation/NotThreadSafe.java b/libtest/src/main/java/z/annotation/NotThreadSafe.java deleted file mode 100644 index ff3b1cd6e..000000000 --- a/libtest/src/main/java/z/annotation/NotThreadSafe.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.annotation; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Documented -@Retention(RetentionPolicy.SOURCE) -public @interface NotThreadSafe { - String value() default ""; -} diff --git a/libtest/src/main/java/z/annotation/PerformanceDegraded.java b/libtest/src/main/java/z/annotation/PerformanceDegraded.java deleted file mode 100644 index eeb70a380..000000000 --- a/libtest/src/main/java/z/annotation/PerformanceDegraded.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.annotation; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Documented -@Retention(RetentionPolicy.SOURCE) -public @interface PerformanceDegraded { - String value() default ""; -} diff --git a/libtest/src/main/java/z/annotation/ThreadSafe.java b/libtest/src/main/java/z/annotation/ThreadSafe.java deleted file mode 100644 index e701f1aad..000000000 --- a/libtest/src/main/java/z/annotation/ThreadSafe.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.annotation; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Documented -@Retention(RetentionPolicy.SOURCE) -public @interface ThreadSafe { - String value() default ""; -} diff --git a/libtest/src/main/java/z/channel/BroadcastChannel.java b/libtest/src/main/java/z/channel/BroadcastChannel.java deleted file mode 100644 index 1dfac6f7d..000000000 --- a/libtest/src/main/java/z/channel/BroadcastChannel.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * BroadcastChannel, is a {@link z.channel.Channel} which support the - * broadcast style message. - */ -public interface BroadcastChannel extends Channel { - public ReceivePort createReceivePort(); -} diff --git a/libtest/src/main/java/z/channel/Channel.java b/libtest/src/main/java/z/channel/Channel.java deleted file mode 100644 index 15525b14c..000000000 --- a/libtest/src/main/java/z/channel/Channel.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Channel, is an abstract for all kinds of Inter-Thread-Communication(ITC) - * mechanism. - *

- * - */ -public interface Channel { - public boolean trySend(T value); - public void send(T value); -} diff --git a/libtest/src/main/java/z/channel/Channels.java b/libtest/src/main/java/z/channel/Channels.java deleted file mode 100644 index 0b5036907..000000000 --- a/libtest/src/main/java/z/channel/Channels.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Channels, is a factory for all kinds of {@link z.channel.Channel}. - *

- * From the aspect of producer/consumer, this factory provides four kinds - * channel: - *

SPSC - - *

SPMC - - *

MPSC - - *

MPMC - - *

- * Different types of channels have different use conditions and different - * performance. - * - */ -public class Channels { - - public static BroadcastChannel createBoundedSPSCChannel(int size) { - return new GenericHyperLoop(size); - } - - public static BroadcastChannel createBoundedSPMCChannel(int size) { - return new GenericHyperLoop(size); - } - - public static Channel createBoundedMPSCChannel(int size) { - return new GenericMPMCQueue(size); - } - - public static Channel createBoundedMPMCChannel(int size) { - return new GenericMPMCQueue(size); - } - -} diff --git a/libtest/src/main/java/z/channel/GenericHyperLoop.java b/libtest/src/main/java/z/channel/GenericHyperLoop.java deleted file mode 100644 index 3cac6c9c4..000000000 --- a/libtest/src/main/java/z/channel/GenericHyperLoop.java +++ /dev/null @@ -1,272 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - -/** - * Generic variant of {@link z.channel.LongHyperLoop} - */ -public final class GenericHyperLoop implements BroadcastChannel { - private static final int BASE = UNSAFE.ARRAY_OBJECT_BASE_OFFSET; - private static final int SCALE = UNSAFE.ARRAY_OBJECT_INDEX_SCALE; - - //TODO: add a config option for SIZE_HYPERLOOP_BASE? - /** - * NOTE: you should reserve 128*(4+number of consumers of LongHyperLoop), - * otherwise you may crash your JVM. - *

- * contract: - * 1. now keep it a multiple of 4096 - */ - private static final int SIZE_HYPERLOOP_BASE = 4096;//only for 28 consumers? - - private final long addressRaw; - private final long addressHyperLoop; - - private final int nBufferSlots; - private final int bufferSlotMask; - - private final long addrWriteCursor; - private final long addrMinReadCursor; -// private final long addrBuffer; - private final long addrReadCursorCount;//int type - private long addrReadCursors; - - private final Object[] slots; - - /** - * create a LongHyperLoop with 512 slots in its internal buffer - * - */ - public GenericHyperLoop() { - this(1024); - } - - /** - * create a LongHyperLoop. - *

- * contract:

- * 1. nBufferSlots >=8

- * 2. nBufferSlots is a power of 2, and - * better if you can make nBufferSlots*8 a multiple of 4096 - * - *

- * - * @param nBufferSlots the size of internal buffer in slot unit - */ - public GenericHyperLoop(int nBufferSlots) { - contract(()->nBufferSlots>=8); - contract(()->Integer.bitCount(nBufferSlots)==1); - this.nBufferSlots = nBufferSlots; - this.bufferSlotMask = nBufferSlots - 1; - //======================================================== - int requestedSize = SIZE_HYPERLOOP_BASE; - addressRaw = systemAllocateMemory(requestedSize + SIZE_PAGE); - - addressHyperLoop = nextPageAlignedAddress(addressRaw); - contract(() -> isPageAligned(addressHyperLoop)); - - this.addrWriteCursor = addressHyperLoop - + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrWriteCursor)); - - this.addrMinReadCursor = addrWriteCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrMinReadCursor)); - - this.addrReadCursorCount = addrMinReadCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrReadCursorCount)); - - this.addrReadCursors = addrReadCursorCount; - contract(() -> isCacheLineAligned(addrReadCursors)); - - //================slots - slots = new Object[nBufferSlots]; - - - UNSAFE.putLong(addrWriteCursor, 0L); - UNSAFE.putLong(addrMinReadCursor, 0L); - UNSAFE.putInt(addrReadCursorCount,0); - } - - /** - * TODO: overload a long array version? - * Send out an Object of type T to the {@link GenericHyperLoop} - * - * @param value - the value to be sent out to the {@link GenericHyperLoop} - * @return - true if sent out successfully, or false if failed - */ - public boolean trySend(T value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - if (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - return false; - } - - UNSAFE.putOrderedObject(slots, - BASE+SCALE*(writeCursor & bufferSlotMask),value); - UNSAFE.putOrderedLong(null,addrWriteCursor, writeCursor + 1); - return true; - } - - /** - * variant of {@link #trySend(Object)}.

- * It will block until the value has been sent out. - */ - public void send(T value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - - while (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - Thread.yield();//harm latency but welcome to throughput - } - - UNSAFE.putObject(slots, - BASE+SCALE*(writeCursor & bufferSlotMask),value); - UNSAFE.putLong(addrWriteCursor, writeCursor + 1); - UNSAFE.storeFence(); - } - - - @Override - public void finalize() { - systemFreeMemory(addressRaw); - } - - - public z.channel.ReceivePort createReceivePort() { - return this.new OutPort(); - } - - /** - * Note: - * it is always hoped the values sent into LongHyperLoop could be consumed ASAP. - * - * TODO: need to handle the removal of OutPort dynamically - */ - public final class OutPort implements z.channel.ReceivePort { - private final long addrReadCursor; - - public OutPort() { - synchronized(OutPort.class) { - addrReadCursors += SIZE_CACHE_LINE_PADDING; - this.addrReadCursor = addrReadCursors; - //TODO: clear the readCursor - UNSAFE.putLong(addrReadCursor,0L); - UNSAFE.putInt(addrReadCursorCount, - UNSAFE.getInt(addrReadCursorCount) + 1); - } - } - - public boolean isReceivable() { - return UNSAFE.getLong(addrReadCursor) != - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - public boolean notReceivable() { - return UNSAFE.getLong(addrReadCursor) == - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - //TODO: unchecked except to checked? - /** - * this {@link #tryReceive()} and {@link #notReceivable()} are another kind - * style of combined APIs.

- * Contrast to {@link #receive()}, - * you should first use {@link #notReceivable()} to ensure that you can - * do the following {@link #tryReceive()}, otherwise you may get an - * {@link IllegalStateException} when nothing could be received. - * - * @return - */ - public T tryReceive() { - long readCursor = UNSAFE.getLong(addrReadCursor); - if (readCursor==UNSAFE.getLong(addrWriteCursor)) { - throw new IllegalStateException("nothing to receive."); - } - //NOTE: we need a load fence to the element of slots, not slots itself - T value = (T)UNSAFE.getObjectVolatile(slots, - BASE + SCALE * (readCursor & bufferSlotMask)); - UNSAFE.putLong(addrReadCursor, readCursor+1); - UNSAFE.storeFence(); - return value; - } - - /** - * Contrast to {@link #notReceivable()} + {@link #tryReceive()}, this call - * will wait(block) until something can be received.

- * So this call gives more higher throughput but harms latency.

- * NOTE: This method is just for lazy men.

- * You can use {@link #notReceivable()} + {@link #tryReceive()} with - * Landz off-heap APIs to achieve more controllable - * and low latency batch behavior. - * - */ - public T receive() { - long readCursor = UNSAFE.getLong(addrReadCursor); - while (readCursor==UNSAFE.getLongVolatile(null, addrWriteCursor)) { - Thread.yield(); - } - T value = (T)UNSAFE.getObjectVolatile(slots, - BASE + SCALE * (readCursor & bufferSlotMask)); - UNSAFE.putLong(addrReadCursor, readCursor+1); - UNSAFE.storeFence(); - return value; - } - - } - -} diff --git a/libtest/src/main/java/z/channel/GenericMPMCQueue.java b/libtest/src/main/java/z/channel/GenericMPMCQueue.java deleted file mode 100644 index 2bceabac4..000000000 --- a/libtest/src/main/java/z/channel/GenericMPMCQueue.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - - -/** - * Generic variant of {@link z.channel.MPMCQueue}. - * - */ -public final class GenericMPMCQueue implements Channel, ReceivePort { - - private static final int BASE = UNSAFE.ARRAY_OBJECT_BASE_OFFSET; - private static final int SCALE = UNSAFE.ARRAY_OBJECT_INDEX_SCALE; - - private static final int LONG_BASE = UNSAFE.ARRAY_LONG_BASE_OFFSET; - private static final int LONG_SCALE = UNSAFE.ARRAY_LONG_INDEX_SCALE; - -// private static final int SIZESHIFT_BUFFERSLOT = 4;//2*SIZE_LONG_TYPE -// private static final int SIZE_LONG_TYPE = 8; - - private final long addressRaw; - private final long addressMPMCQueue; - - private final int nBufferSlots; - private final int bufferSlotMask; - - private final long addrWriteCursor; - private final long addrReadCursor; - - private final Object[] slots; - private final long[] slotSeqs; - -// //1 boolean(byte) per slot,true is un-pub-ed, false is pub-ed -// private final long addrMarkBuffer; - - /** - * @param nBufferSlots the size of internal buffer in slot unit - */ - public GenericMPMCQueue(int nBufferSlots) { - contract(()->nBufferSlots>=2); - contract(()->Integer.bitCount(nBufferSlots)==1); - this.nBufferSlots = nBufferSlots; - this.bufferSlotMask = nBufferSlots - 1; - //======================================================== - addressRaw = systemAllocateMemory(SIZE_CACHE_LINE_PADDING*4 + SIZE_CACHE_LINE); - - addressMPMCQueue = nextCacheLineAlignedAddress(addressRaw); - contract(() -> isCacheLineAligned(addressMPMCQueue)); - - this.addrWriteCursor = addressMPMCQueue + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrWriteCursor)); - - this.addrReadCursor = addrWriteCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrReadCursor)); - - slots = new Object[nBufferSlots]; - - slotSeqs = new long[nBufferSlots]; - for (int i = 0; i < nBufferSlots; i++) { - UNSAFE.putLongVolatile(slotSeqs, LONG_BASE+LONG_SCALE*i, i); - } - - UNSAFE.putLong(addrWriteCursor, 0L); - UNSAFE.putLong(addrReadCursor, 0L); - } - - public void send(T value) { - while (!offer(value)){ - Thread.yield();//TODO - } - } - - public boolean trySend(T value) { - return offer(value); - } - - public T tryReceive() { - return poll(); - } - - public T receive() { - T v; - while ( null==(v=poll()) ) { - Thread.yield();//TODO - } - return v; - } - - public boolean isReceivable() { - throw - new UnsupportedOperationException("isReceivable is not available now"); - } - - public boolean notReceivable() { - throw - new UnsupportedOperationException("notReceivable is not available now"); - } - - /** - * To add one long value to current {@link z.channel.GenericMPMCQueue}. - * The added long value will stored in the {@link z.channel.GenericMPMCQueue} - * in FIFO order. - *

- * @return - true if successfully added to the currrent - * {@link z.channel.GenericMPMCQueue}, - * or false if currrent {@link z.channel.GenericMPMCQueue} - * is full. - */ - public boolean offer(T value) { - long writeCursor, seqOffset; - writeCursor = UNSAFE.getLongVolatile(null,addrWriteCursor); - for (;;) { - seqOffset = LONG_BASE+LONG_SCALE*(writeCursor & bufferSlotMask); - long seq = UNSAFE.getLongVolatile(slotSeqs, seqOffset); - long dif = seq - writeCursor; - if (dif == 0) { - if (UNSAFE.compareAndSwapLong(null, - addrWriteCursor, writeCursor, writeCursor + 1)) - break; - } else if (dif < 0) { - return false; - } else { - writeCursor = UNSAFE.getLongVolatile(null,addrWriteCursor); - } - } - UNSAFE.putOrderedObject(slots, - BASE+SCALE*(writeCursor & bufferSlotMask), value); - UNSAFE.putLongVolatile(slotSeqs, seqOffset, writeCursor + 1); - - return true; - } - - /** - * To Retrieves and removes one long value from current - * {@link z.channel.GenericMPMCQueue}. - * - * This long value is sent before in FIFO order. - *

- * @return - one long value if currrent {@link z.channel.GenericMPMCQueue} - * holds at least one value, - * or null if currrent {@link z.channel.GenericMPMCQueue} - * is empty. Please use the null to check your result. - */ - public T poll() { - long readCursor, seqOffset; - readCursor = UNSAFE.getLongVolatile(null,addrReadCursor); - for (;;) { - seqOffset = LONG_BASE+LONG_SCALE*(readCursor & bufferSlotMask); - long seq = UNSAFE.getLongVolatile(slotSeqs, seqOffset); - long dif = seq - (readCursor + 1); - if (dif == 0) { - if (UNSAFE.compareAndSwapLong(null, - addrReadCursor, readCursor, readCursor + 1)) - break; - } else if (dif < 0) { - return null; - } else { - readCursor = UNSAFE.getLongVolatile(null, addrReadCursor); - } - } - - T value = (T)UNSAFE.getObjectVolatile(slots, - BASE+SCALE*(readCursor & bufferSlotMask)); - UNSAFE.putLongVolatile(slotSeqs, seqOffset, readCursor + nBufferSlots); - return value; - } - - @Override - public void finalize() { - systemFreeMemory(addressRaw); - } - -} diff --git a/libtest/src/main/java/z/channel/IntHyperLoop.java b/libtest/src/main/java/z/channel/IntHyperLoop.java deleted file mode 100644 index e974ef719..000000000 --- a/libtest/src/main/java/z/channel/IntHyperLoop.java +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - -/** - * {@link z.channel.IntHyperLoop} is an Inter-Thread-Communication(ITC) mechanism. - *

- * Note:

- * now, the LongHyperLoop aims to support Landz's Asyncor async programming - * model. For performance, the current implementations of LongHyperLoop use - * off-heap memory via s.m.Unsafe, so it is a little "heavy", take care - * when you rush with "new/discard" to LongHyperLoop. - *

see more - * - */ -public class IntHyperLoop { - //TODO: add a config option for SIZE_HYPERLOOP_BASE? - /** - * NOTE: you should reserve 128*(4+number of consumers of LongHyperLoop), - * otherwise you may crash your JVM. - *

- * contract: - * 1. now keep it a multiple of 4096 - */ - private static final int SIZE_HYPERLOOP_BASE = 4096;//only for 28 consumers? - private static final int SIZE_SHIFT_BUFFER_SLOT = 2; -// private static final int SIZE_INT_TYPE = 4; - - private final long addressRaw; - private final long addressHyperLoop; - - private final int nBufferSlots; - private final int bufferSlotMask; - - private final long addrWriteCursor; - private final long addrMinReadCursor; - private final long addrBuffer; - private final long addrReadCursorCount;//int type - private long addrReadCursors; - - /** - * create a LongHyperLoop with 512 slots in its internal buffer - * - */ - public IntHyperLoop() { - this(1024); - } - - /** - * create a LongHyperLoop. - *

- * contract:

- * 1. nBufferSlots >=8

- * 2. nBufferSlots is a power of 2, and - * better if you can make nBufferSlots*8 a multiple of 4096 - * - *

- * - * @param nBufferSlots the size of internal buffer in slot unit - */ - public IntHyperLoop(int nBufferSlots) { - contract(()->nBufferSlots>=8); - contract(()->Integer.bitCount(nBufferSlots)==1); - this.nBufferSlots = nBufferSlots; - this.bufferSlotMask = nBufferSlots - 1; - //======================================================== - int bufferSize = nBufferSlots<< SIZE_SHIFT_BUFFER_SLOT; - int requestedSize = bufferSize + SIZE_HYPERLOOP_BASE; - addressRaw = systemAllocateMemory(requestedSize + SIZE_PAGE); - - addressHyperLoop = nextPageAlignedAddress(addressRaw); - contract(() -> isPageAligned(addressHyperLoop)); - - this.addrWriteCursor = addressHyperLoop - + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrWriteCursor)); - - this.addrMinReadCursor = addrWriteCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrMinReadCursor)); - - this.addrReadCursorCount = addrMinReadCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrReadCursorCount)); - - this.addrReadCursors = addrReadCursorCount; - contract(() -> isCacheLineAligned(addrReadCursors)); - - this.addrBuffer = addressHyperLoop + SIZE_HYPERLOOP_BASE; - //assumed ReadCursors gives enough space - //in fact, the follow is not strictly guaranteed but still hoped it kept - contract(() -> isCacheLineAligned(addrBuffer)); - - UNSAFE.putLong(addrWriteCursor, 0L); - UNSAFE.putLong(addrMinReadCursor, 0L); - UNSAFE.putInt(addrReadCursorCount,0); - } - - /** - * TODO: overload a long array version? - * @param value - * @return - */ - public boolean trySend(int value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - if (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - return false; - } - - UNSAFE.putInt( - addrBuffer + ((writeCursor & bufferSlotMask) << SIZE_SHIFT_BUFFER_SLOT), - value); - UNSAFE.putLong(addrWriteCursor, writeCursor + 1); - UNSAFE.storeFence(); - return true; - } - - - public void send(int value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - - while (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - Thread.yield();//harm latency but welcome to throughput - } - - UNSAFE.putInt( - addrBuffer + ((writeCursor & bufferSlotMask) << SIZE_SHIFT_BUFFER_SLOT), - value); - UNSAFE.putLong(addrWriteCursor, writeCursor + 1); - UNSAFE.storeFence(); - } - - - @Override - public void finalize() { - systemFreeMemory(addressRaw); - } - - public OutPort createOutPort() { - return this.new OutPort(); - } - - /** - * Note: - * it is always hoped the values sent into LongHyperLoop could be consumed ASAP. - * - * TODO: need to handle the removal of OutPort dynamically - */ - public final class OutPort { - private final long addrReadCursor; - - private OutPort() { - synchronized(OutPort.class) { - addrReadCursors += SIZE_CACHE_LINE_PADDING; - this.addrReadCursor = addrReadCursors; - //TODO: clear the readCursor - UNSAFE.putLong(addrReadCursor,0L); - UNSAFE.putInt(addrReadCursorCount, - UNSAFE.getInt(addrReadCursorCount) + 1); - } - } - - public boolean isReceivable() { - return UNSAFE.getLong(addrReadCursor) != - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - public boolean notReceivable() { - return UNSAFE.getLong(addrReadCursor) == - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - //TODO: unchecked except to checked? - /** - * this {@link #receive()} and {@link #notReceivable()} are another kind - * style of combined APIs.

- * Contrast to {@link #received()}, - * you should first use {@link #notReceivable()} to ensure that you can - * do the following {@link #receive()}, otherwise you may get an - * {@link IllegalStateException} when nothing could be received. - * - * @return - */ - public int receive() { -// UNSAFE.loadFence(); - long readCursor = UNSAFE.getLong(addrReadCursor); - if (readCursor==UNSAFE.getLong(addrWriteCursor)) { - throw new IllegalStateException("nothing to receive."); - } - int value = UNSAFE.getInt( - addrBuffer + ((readCursor & bufferSlotMask)<< SIZE_SHIFT_BUFFER_SLOT)); - UNSAFE.putLong(addrReadCursor, readCursor+1); - UNSAFE.storeFence(); - return value; - } - - /** - * Contrast to {@link #notReceivable()} + {@link #receive()}, this call - * will wait(block) until something can be received.

- * So this call gives more higher throughput but harms latency.

- * NOTE: This method is just for lazy men.

- * You can use {@link #notReceivable()} + {@link #receive()} with - * Landz off-heap APIs to achieve more controllable - * and low latency batch behavior. - * - */ - public int received() { - long readCursor = UNSAFE.getLong(addrReadCursor); - while (readCursor==UNSAFE.getLongVolatile(null, addrWriteCursor)) { - Thread.yield(); - } - int value = UNSAFE.getInt( - addrBuffer + ((readCursor & bufferSlotMask)<< SIZE_SHIFT_BUFFER_SLOT)); - UNSAFE.putLong(addrReadCursor, readCursor+1); - UNSAFE.storeFence(); - return value; - } - - } - -// @FunctionalInterface -// public static interface WaitStrategy { -// public void waitToReceive(); -// } - -} diff --git a/libtest/src/main/java/z/channel/LongHyperLoop.java b/libtest/src/main/java/z/channel/LongHyperLoop.java deleted file mode 100644 index 80b93c984..000000000 --- a/libtest/src/main/java/z/channel/LongHyperLoop.java +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - -/** - * {@link LongHyperLoop} is an Inter-Thread-Communication(ITC) mechanism. - *

- * Note:

- * now, the LongHyperLoop aims to support Landz's Asyncor async programming - * model. For performance, the current implementations of LongHyperLoop use - * off-heap memory via s.m.Unsafe, so it is a little "heavy", take care - * when you rush with "new/discard" to LongHyperLoop. - *

see more - * - */ -public class LongHyperLoop { - //TODO: add a config option for SIZE_HYPERLOOP_BASE? - /** - * NOTE: you should reserve 128*(4+number of consumers of LongHyperLoop), - * otherwise you may crash your JVM. - *

- * contract: - * 1. now keep it a multiple of 4096 - */ - private static final int SIZE_HYPERLOOP_BASE = 4096;//only for 28 consumers? - private static final int SIZE_SHIFT_BUFFERSLOT = 3; - private static final int SIZE_LONG_TYPE = 8; - - private final long addressRaw; - private final long addressHyperLoop; - - private final int nBufferSlots; - private final int bufferSlotMask; - - private final long addrWriteCursor; - private final long addrMinReadCursor; - private final long addrBuffer; - private final long addrReadCursorCount;//int type - private long addrReadCursors; - - /** - * create a LongHyperLoop with 512 slots in its internal buffer - * - */ - public LongHyperLoop() { - this(512); - } - - /** - * create a LongHyperLoop. - *

- * contract:

- * 1. nBufferSlots >=8

- * 2. nBufferSlots is a power of 2, and - * better if you can make nBufferSlots*8 a multiple of 4096 - * - *

- * - * @param nBufferSlots the size of internal buffer in slot unit - */ - public LongHyperLoop(int nBufferSlots) { - contract(()->nBufferSlots>=8); - contract(()->Integer.bitCount(nBufferSlots)==1); - this.nBufferSlots = nBufferSlots; - this.bufferSlotMask = nBufferSlots - 1; - //======================================================== - int bufferSize = nBufferSlots< isPageAligned(addressHyperLoop)); - - this.addrWriteCursor = addressHyperLoop - + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrWriteCursor)); - - this.addrMinReadCursor = addrWriteCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrMinReadCursor)); - - this.addrReadCursorCount = addrMinReadCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrReadCursorCount)); - - this.addrReadCursors = addrReadCursorCount; - contract(() -> isCacheLineAligned(addrReadCursors)); - - this.addrBuffer = addressHyperLoop + SIZE_HYPERLOOP_BASE; - //assumed ReadCursors gives enough space - //in fact, the follow is not strictly guaranteed but still hoped it kept - contract(() -> isCacheLineAligned(addrBuffer)); - - UNSAFE.putLong(addrWriteCursor, 0L); - UNSAFE.putLong(addrMinReadCursor, 0L); - UNSAFE.putInt(addrReadCursorCount,0); - } - - /** - * TODO: overload a long array version? - * @param value - * @return - */ - public boolean send(long value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - if (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - return false; - } - - UNSAFE.putLong( - addrBuffer + ((writeCursor & bufferSlotMask) << SIZE_SHIFT_BUFFERSLOT), - value); - UNSAFE.putLong(addrWriteCursor, writeCursor + 1); - UNSAFE.storeFence(); - return true; - } - - - public void sendTo(long value) { - long minReadCursor = UNSAFE.getLongVolatile(null,addrMinReadCursor); - long writeCursor = UNSAFE.getLong(addrWriteCursor); - - while (writeCursor == (minReadCursor+nBufferSlots)) { - // assume readCursorCount>0 - minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); - for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { - long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors - - i*SIZE_CACHE_LINE_PADDING); - minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; - } - UNSAFE.putLong(addrMinReadCursor,minReadCursor); - Thread.yield();//harm latency but welcome to throughput - } - //TODO: - UNSAFE.putLong( - addrBuffer + ((writeCursor & bufferSlotMask) << SIZE_SHIFT_BUFFERSLOT), - value); - UNSAFE.putLong(addrWriteCursor, writeCursor + 1); - UNSAFE.storeFence(); - } - - - @Override - public void finalize() { - systemFreeMemory(addressRaw); - } - - public OutPort createOutPort() { - return this.new OutPort(); - } - - /** - * Note: - * it is always hoped the values sent into LongHyperLoop could be consumed ASAP. - * - * TODO: need to handle the removal of OutPort dynamically - */ - public final class OutPort { - private final long addrReadCursor; - - private OutPort() { - synchronized(OutPort.class) { - addrReadCursors += SIZE_CACHE_LINE_PADDING; - this.addrReadCursor = addrReadCursors; - //TODO: clear the readCursor - UNSAFE.putLong(addrReadCursor,0L); - UNSAFE.putInt(addrReadCursorCount, - UNSAFE.getInt(addrReadCursorCount) + 1); - } - } - - public boolean isReceivable() { - return UNSAFE.getLong(addrReadCursor) != - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - public boolean notReceivable() { - return UNSAFE.getLong(addrReadCursor) == - UNSAFE.getLongVolatile(null, addrWriteCursor); - } - - //TODO: unchecked except to checked? - /** - * this {@link #receive()} and {@link #notReceivable()} are another kind - * style of combined APIs.

- * Contrast to {@link #received()}, - * you should first use {@link #notReceivable()} to ensure that you can - * do the following {@link #receive()}, otherwise you may get an - * {@link IllegalStateException} when nothing could be received. - * - * @return - */ - public long receive() { -// UNSAFE.loadFence(); - long readCursor = UNSAFE.getLong(addrReadCursor); - if (readCursor==UNSAFE.getLong(addrWriteCursor)) { - throw new IllegalStateException("nothing to receive."); - } - //TODO: getLong, getLongVolatile, getAddress seems... - long value = UNSAFE.getLongVolatile(null, - addrBuffer + ((readCursor & bufferSlotMask)< - * So this call gives more higher throughput but harms latency.

- * NOTE: This method is just for lazy men.

- * You can use {@link #notReceivable()} + {@link #receive()} with - * Landz off-heap APIs to achieve more controllable - * and low latency batch behavior. - * - */ - public long received() { - long readCursor = UNSAFE.getLong(addrReadCursor); - while (readCursor==UNSAFE.getLongVolatile(null, addrWriteCursor)) { - Thread.yield(); - } - long value = UNSAFE.getLong( - addrBuffer + ((readCursor & bufferSlotMask)<. - * #L% - */ - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - - -/** - * A bounded multi-producer/multi-consumer LongHyperLoop. - *

- * Different from MP Disruptor, it is done in "queue" style. That is, the out - * element is only consumed by one of all consumers, not all of them - * (broadcasting). - *

- * references implementation is Dmitry Vyukov's Bounded MPMC queue: - * http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue - *

- * Note:

- * 1. this is NOT a "totally lock-free" algorithm. But, it's still much - * better than the lock-based algorithm. - * 2. for convenience, now we use {@link Long#MIN_VALUE} to present NULL - * to receive from {@link MPMCQueue}(otherwise we should an exception). - * - */ -public class MPMCQueue { - - /** - * for convenience, now we use {@link Long#MIN_VALUE} to present nothing could - * be received from {@link MPMCQueue}(otherwise we should an exception). - */ - public static final long NULL = Long.MIN_VALUE; - private static final int SIZESHIFT_BUFFERSLOT = 4;//2*SIZE_LONG_TYPE - private static final int SIZE_LONG_TYPE = 8; - - private final long addressRaw; - private final long addressMPMCQueue; - - private final int nBufferSlots; - private final int bufferSlotMask; - - private final long addrWriteCursor; - private final long addrReadCursor; - private final long addrBuffer; - -// //1 boolean(byte) per slot,true is un-pub-ed, false is pub-ed -// private final long addrMarkBuffer; - - /** - * @param nBufferSlots the size of internal buffer in slot unit - */ - public MPMCQueue(int nBufferSlots) { - contract(()->nBufferSlots>=2); - contract(()->Integer.bitCount(nBufferSlots)==1); - this.nBufferSlots = nBufferSlots; - this.bufferSlotMask = nBufferSlots - 1; - //======================================================== - int bufferSize = nBufferSlots< isCacheLineAligned(addressMPMCQueue)); - - this.addrWriteCursor = addressMPMCQueue + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrWriteCursor)); - - this.addrReadCursor = addrWriteCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrReadCursor)); - - this.addrBuffer = addrReadCursor + SIZE_CACHE_LINE_PADDING; - contract(() -> isCacheLineAligned(addrBuffer)); - -// this.addrMarkBuffer = addrBuffer + bufferSize + SIZE_CACHE_LINE_PADDING; -// contract(() -> isCacheLineAligned(addrMarkBuffer)); - - //clear - for (int i = 0; i < nBufferSlots; i++) { - UNSAFE.putLong( - addrBuffer + (i << SIZESHIFT_BUFFERSLOT)+SIZE_LONG_TYPE, i); - } - - UNSAFE.putLong(addrWriteCursor, 0L); - UNSAFE.putLong(addrReadCursor, 0L); - } - - /** - * To add one long value to current {@link MPMCQueue}. - * The added long value will stored in the {@link MPMCQueue} - * in FIFO order. - *

- * @return - true if successfully added to the currrent - * {@link MPMCQueue}, - * or false if currrent {@link MPMCQueue} - * is full. - */ - public boolean offer(long value) { - long writeCursor, addrWriteIndex; - writeCursor = UNSAFE.getLongVolatile(null,addrWriteCursor); - for (;;) { - addrWriteIndex = addrBuffer + - ((writeCursor & bufferSlotMask) << SIZESHIFT_BUFFERSLOT); - long seq = UNSAFE.getLongVolatile(null, addrWriteIndex+SIZE_LONG_TYPE); - long dif = seq - writeCursor; - if (dif == 0) { - if (UNSAFE.compareAndSwapLong(null, - addrWriteCursor, writeCursor, writeCursor + 1)) - break; - } else if (dif < 0) { - return false; - } else { - writeCursor = UNSAFE.getLongVolatile(null,addrWriteCursor); - } - } - UNSAFE.putLongVolatile(null, addrWriteIndex,value); - UNSAFE.putLongVolatile(null, addrWriteIndex + SIZE_LONG_TYPE, - writeCursor + 1); - - return true; - } - - /** - * To Retrieves and removes one long value from current - * {@link MPMCQueue}. - * - * This long value is sent before in FIFO order. - *

- * @return - one long value if currrent {@link MPMCQueue} - * holds at least one value, - * or {@link #NULL} if currrent {@link MPMCQueue} - * is empty. Please use the {@link #NULL} to check your result. - */ - public long poll() { - long readCursor, addrReadIndex; - readCursor = UNSAFE.getLongVolatile(null,addrReadCursor); - for (; ; ) { - addrReadIndex = addrBuffer + - ((readCursor & bufferSlotMask) << SIZESHIFT_BUFFERSLOT); - long seq = UNSAFE.getLongVolatile(null, addrReadIndex + SIZE_LONG_TYPE); - long dif = seq - (readCursor + 1); - if (dif == 0) { - if (UNSAFE.compareAndSwapLong(null, - addrReadCursor, readCursor, readCursor + 1)) - break; - } else if (dif < 0) { - return NULL; - } else { - readCursor = UNSAFE.getLongVolatile(null, addrReadCursor); - } - } - - long value = UNSAFE.getLongVolatile(null, addrReadIndex); - UNSAFE.putLongVolatile(null, addrReadIndex + SIZE_LONG_TYPE, - readCursor + nBufferSlots); - return value; - } - - @Override - public void finalize() { - systemFreeMemory(addressRaw); - } - -} diff --git a/libtest/src/main/java/z/channel/ReceivePort.java b/libtest/src/main/java/z/channel/ReceivePort.java deleted file mode 100644 index 9b07a7e7c..000000000 --- a/libtest/src/main/java/z/channel/ReceivePort.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.channel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * OutPort, is used to express outside endpoints of {@link Channel}s which - * support the broadcast style message. - * - */ -public interface ReceivePort { - public T receive(); - public T tryReceive(); - public boolean isReceivable(); - public boolean notReceivable(); -} diff --git a/libtest/src/main/java/z/evil/Intrinsics.java b/libtest/src/main/java/z/evil/Intrinsics.java deleted file mode 100644 index aec72cf8e..000000000 --- a/libtest/src/main/java/z/evil/Intrinsics.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.evil; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class Intrinsics { - - /** - * Tricks: - * warmup several static methods around the whole Landz, - * to enable the early join of Hotspot intrinsics. - * This tricks is not much useful for long-run instances. - * - * NOTE: here, it is assumed that intrinsification is more early than - * other optimizations. - */ - public static final void warmup() { -// int COUNT = 5000_000; -// for (int i = 0; i < COUNT; i++) { -// Allocator.sizeClassIndex(i); -// } - } - - -} diff --git a/libtest/src/main/java/z/exception/ContractViolatedException.java b/libtest/src/main/java/z/exception/ContractViolatedException.java deleted file mode 100644 index f02064a7a..000000000 --- a/libtest/src/main/java/z/exception/ContractViolatedException.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.exception; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -public class ContractViolatedException - extends IllegalStateException -{ - - private static final long serialVersionUID = 440746453796123497L; - - /** - * Constructs an instance of this class. - */ - public ContractViolatedException() { - } - - /** - * Constructs an instance of this class with the specified detailed message. - */ - public ContractViolatedException(String msg) { - super(msg); - } -} \ No newline at end of file diff --git a/libtest/src/main/java/z/function/ExceptionalFunction0.java b/libtest/src/main/java/z/function/ExceptionalFunction0.java deleted file mode 100644 index 344e10b9f..000000000 --- a/libtest/src/main/java/z/function/ExceptionalFunction0.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters which throws Exception. - * () -> R throws Exception - * @param - */ -@FunctionalInterface -public interface ExceptionalFunction0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - * - * @throws Exception - */ - R apply() throws Exception; - -} diff --git a/libtest/src/main/java/z/function/ExceptionalRunnable.java b/libtest/src/main/java/z/function/ExceptionalRunnable.java deleted file mode 100644 index 3cb03454f..000000000 --- a/libtest/src/main/java/z/function/ExceptionalRunnable.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function, specialization for Runnable, which throws Exception. - * () -> void throws Exception - */ -@FunctionalInterface -public interface ExceptionalRunnable { - - /** - * Return the result of applying the lambda. - * - * @throws Exception; - */ - void run() throws Exception; - -} diff --git a/libtest/src/main/java/z/function/Function0.java b/libtest/src/main/java/z/function/Function0.java deleted file mode 100644 index 614fc1417..000000000 --- a/libtest/src/main/java/z/function/Function0.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters. - * () -> R - * @param - */ -@FunctionalInterface -public interface Function0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(); - -} diff --git a/libtest/src/main/java/z/function/Function1.java b/libtest/src/main/java/z/function/Function1.java deleted file mode 100644 index 003cfe3a0..000000000 --- a/libtest/src/main/java/z/function/Function1.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 1 parameters. - * (T) -> R - * @param - */ -@FunctionalInterface -public interface Function1 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T v); - -} diff --git a/libtest/src/main/java/z/function/Function10.java b/libtest/src/main/java/z/function/Function10.java deleted file mode 100644 index 4a2dccbed..000000000 --- a/libtest/src/main/java/z/function/Function10.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 8 parameters. - * (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) -> R - * @param - */ -@FunctionalInterface -public interface Function10 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10); - -} diff --git a/libtest/src/main/java/z/function/Function11.java b/libtest/src/main/java/z/function/Function11.java deleted file mode 100644 index 82a6b9632..000000000 --- a/libtest/src/main/java/z/function/Function11.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 8 parameters. - * (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) -> R - * @param - */ -@FunctionalInterface -public interface Function11 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11); - -} diff --git a/libtest/src/main/java/z/function/Function2.java b/libtest/src/main/java/z/function/Function2.java deleted file mode 100644 index 973fb5974..000000000 --- a/libtest/src/main/java/z/function/Function2.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 2 parameters. - * (T1, T2) -> R - * @param - */ -@FunctionalInterface -public interface Function2 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2); - -} diff --git a/libtest/src/main/java/z/function/Function3.java b/libtest/src/main/java/z/function/Function3.java deleted file mode 100644 index 5e87f5e66..000000000 --- a/libtest/src/main/java/z/function/Function3.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 3 parameters. - * (T1, T2, T3) -> R - * @param - */ -@FunctionalInterface -public interface Function3 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3); - -} diff --git a/libtest/src/main/java/z/function/Function4.java b/libtest/src/main/java/z/function/Function4.java deleted file mode 100644 index 88b8063ac..000000000 --- a/libtest/src/main/java/z/function/Function4.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 4 parameters. - * (T1, T2, T3, T4) -> R - * @param - */ -@FunctionalInterface -public interface Function4 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4); - -} diff --git a/libtest/src/main/java/z/function/Function5.java b/libtest/src/main/java/z/function/Function5.java deleted file mode 100644 index 4de4d8b7d..000000000 --- a/libtest/src/main/java/z/function/Function5.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 5 parameters. - * (T1, T2, T3, T4, T5) -> R - * @param - */ -@FunctionalInterface -public interface Function5 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5); - -} diff --git a/libtest/src/main/java/z/function/Function6.java b/libtest/src/main/java/z/function/Function6.java deleted file mode 100644 index 687374d89..000000000 --- a/libtest/src/main/java/z/function/Function6.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 6 parameters. - * (T1, T2, T3, T4, T5, T6) -> R - * @param - */ -@FunctionalInterface -public interface Function6 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6); - -} diff --git a/libtest/src/main/java/z/function/Function7.java b/libtest/src/main/java/z/function/Function7.java deleted file mode 100644 index c5362107d..000000000 --- a/libtest/src/main/java/z/function/Function7.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 7 parameters. - * (T1, T2, T3, T4, T5, T6, T7) -> R - * @param - */ -@FunctionalInterface -public interface Function7 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7); - -} diff --git a/libtest/src/main/java/z/function/Function8.java b/libtest/src/main/java/z/function/Function8.java deleted file mode 100644 index 463bd0877..000000000 --- a/libtest/src/main/java/z/function/Function8.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 8 parameters. - * (T1, T2, T3, T4, T5, T6, T7, T8) -> R - * @param - */ -@FunctionalInterface -public interface Function8 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8); - -} diff --git a/libtest/src/main/java/z/function/Function9.java b/libtest/src/main/java/z/function/Function9.java deleted file mode 100644 index 60785bfd5..000000000 --- a/libtest/src/main/java/z/function/Function9.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 8 parameters. - * (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R - * @param - */ -@FunctionalInterface -public interface Function9 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - R apply(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9); - -} diff --git a/libtest/src/main/java/z/function/Functions.java b/libtest/src/main/java/z/function/Functions.java deleted file mode 100644 index 994c4ce66..000000000 --- a/libtest/src/main/java/z/function/Functions.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.function.BooleanSupplier; - -/** - * Static utility methods kinds of Funcitons . - * - */ -public class Functions { - - private Functions() {} - - /** - * Returns a function whose {@code apply} method returns the provided - * input. Useful when a {@code Function} is required and {@code } - * and {@code } are the same type. - */ - public static BooleanSupplier toBooleanSupplier(ToBooleanFunction0 f) { - return () -> f.apply(); - } - - -} diff --git a/libtest/src/main/java/z/function/Pipe.java b/libtest/src/main/java/z/function/Pipe.java deleted file mode 100644 index 15a68b90a..000000000 --- a/libtest/src/main/java/z/function/Pipe.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.function.Function; - -public class Pipe { - - Function function; - Pipe next; - - public Pipe(Function function) { - this.function = function; - } - - void next(Pipe next) { - this.next = next; - } - -} \ No newline at end of file diff --git a/libtest/src/main/java/z/function/Pipeline.java b/libtest/src/main/java/z/function/Pipeline.java deleted file mode 100644 index 9e39132c8..000000000 --- a/libtest/src/main/java/z/function/Pipeline.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.function.Function; - -/** - * - * TODO: the logic of type inferencing still has some flaws, although works - * basically. - * - */ -public class Pipeline implements Function { - - Pipe head; - Pipe tail; - - private Pipeline(Pipe head) { - this.head = head; - this.tail = head; - } - - public static Pipeline create(Function head){ - //TODO contract(head!=null) - return new Pipeline(new Pipe(head)); - } - - public Pipeline next(Function f) { - Pipe np = new Pipe(f); - tail.next(np); - tail = np; - return (Pipeline)this; - } - - public Pipeline end() { - return (Pipeline)this; - } - - @Override - public OUT apply(IN in) { - Pipe n = head; - Object r = n.function.apply(in); - while ( (n = n.next)!=null ) { - r = n.function.apply(r); - } - return (OUT)r; - } -} diff --git a/libtest/src/main/java/z/function/ThrowableFunction0.java b/libtest/src/main/java/z/function/ThrowableFunction0.java deleted file mode 100644 index 9dbdce3de..000000000 --- a/libtest/src/main/java/z/function/ThrowableFunction0.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters which throws Throwable. - * () -> R throws Throwable - * @param - */ -@FunctionalInterface -public interface ThrowableFunction0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - * - * @throws Throwable - */ - R apply() throws Throwable; - -} diff --git a/libtest/src/main/java/z/function/ThrowableRunnable.java b/libtest/src/main/java/z/function/ThrowableRunnable.java deleted file mode 100644 index 72d38c8b3..000000000 --- a/libtest/src/main/java/z/function/ThrowableRunnable.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function, specialization for Runnable, which throws Throwable. - * () -> void throws Throwable - */ -@FunctionalInterface -public interface ThrowableRunnable { - - /** - * Return the result of applying the lambda. - * - * @throws Throwable; - */ - void run() throws Throwable; - -} diff --git a/libtest/src/main/java/z/function/ToBooleanFunction0.java b/libtest/src/main/java/z/function/ToBooleanFunction0.java deleted file mode 100644 index db8339fe1..000000000 --- a/libtest/src/main/java/z/function/ToBooleanFunction0.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for boolean return. - * () -> boolean - */ -@FunctionalInterface -public interface ToBooleanFunction0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - boolean apply(); - -} diff --git a/libtest/src/main/java/z/function/ToByteFunction0.java b/libtest/src/main/java/z/function/ToByteFunction0.java deleted file mode 100644 index ce3b5a1c2..000000000 --- a/libtest/src/main/java/z/function/ToByteFunction0.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for byte return. - * () -> byte - */ -@FunctionalInterface -public interface ToByteFunction0 { - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - byte apply(); - -} diff --git a/libtest/src/main/java/z/function/ToCharFunction0.java b/libtest/src/main/java/z/function/ToCharFunction0.java deleted file mode 100644 index 1c9681792..000000000 --- a/libtest/src/main/java/z/function/ToCharFunction0.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for char return. - * () -> char - */ -@FunctionalInterface -public interface ToCharFunction0 { - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - char apply(); - -} diff --git a/libtest/src/main/java/z/function/ToDoubleFunction0.java b/libtest/src/main/java/z/function/ToDoubleFunction0.java deleted file mode 100644 index a19606141..000000000 --- a/libtest/src/main/java/z/function/ToDoubleFunction0.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for double return. - * () -> double - */ -@FunctionalInterface -public interface ToDoubleFunction0 { - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - double apply(); - -} diff --git a/libtest/src/main/java/z/function/ToFloatFunction0.java b/libtest/src/main/java/z/function/ToFloatFunction0.java deleted file mode 100644 index 000eabcf0..000000000 --- a/libtest/src/main/java/z/function/ToFloatFunction0.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for float return. - * () -> float - */ -@FunctionalInterface -public interface ToFloatFunction0 { - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - float apply(); - -} diff --git a/libtest/src/main/java/z/function/ToIntFunction0.java b/libtest/src/main/java/z/function/ToIntFunction0.java deleted file mode 100644 index e01669267..000000000 --- a/libtest/src/main/java/z/function/ToIntFunction0.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for int return. - * () -> int - */ -@FunctionalInterface -public interface ToIntFunction0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - int apply(); - -} diff --git a/libtest/src/main/java/z/function/ToLongFunction0.java b/libtest/src/main/java/z/function/ToLongFunction0.java deleted file mode 100644 index 1cb7032e9..000000000 --- a/libtest/src/main/java/z/function/ToLongFunction0.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for long return. - * () -> long - */ -@FunctionalInterface -public interface ToLongFunction0 { - - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - long apply(); - -} diff --git a/libtest/src/main/java/z/function/ToShortFunction0.java b/libtest/src/main/java/z/function/ToShortFunction0.java deleted file mode 100644 index 70775d6ea..000000000 --- a/libtest/src/main/java/z/function/ToShortFunction0.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.function; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Function of 0 parameters, specialization for short return. - * () -> short - */ -@FunctionalInterface -public interface ToShortFunction0 { - /** - * Return the result of applying the lambda. - * - * @return the function result - */ - short apply(); - -} diff --git a/libtest/src/main/java/z/module/License.java b/libtest/src/main/java/z/module/License.java deleted file mode 100644 index 1bd7faa7e..000000000 --- a/libtest/src/main/java/z/module/License.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.module; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * - * refs: - * http://en.wikipedia.org/wiki/Comparison_of_free_software_licenses - */ -public enum License { - - APLv2( - "http://www.apache.org/licenses/LICENSE-2.0", - "Apache License, Version 2.0"), - NewBSD( - "http://opensource.org/licenses/BSD-3-Clause", - "BSD 3-Clause License"), - SimplifiedBSD( - "http://opensource.org/licenses/BSD-2-Clause", - "BSD 2-Clause License"), - MIT( - "http://opensource.org/licenses/mit-license.html", - "MIT License"), - EPLv1( - "http://www.eclipse.org/org/documents/epl-v10.php", - "Eclipse Public License - v 1.0"), - GPLv3( - "http://www.gnu.org/licenses/gpl.html", - "GNU GENERAL PUBLIC LICENSE"), - LGPLv3( - "http://www.gnu.org/licenses/lgpl.html", - "GNU LESSER GENERAL PUBLIC LICENSE"), - - UNKNOWN( - "UNKNOWN", - "UNKNOWN"); - - private final String url; - private final String description; - - License(String url, String description) { - this.url = url; - this.description = description; - } - - public String getUrl() { - return url; - } - public String getDescription() { - return description; - } - -} diff --git a/libtest/src/main/java/z/module/Module.java b/libtest/src/main/java/z/module/Module.java deleted file mode 100644 index 60e9f83a5..000000000 --- a/libtest/src/main/java/z/module/Module.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.module; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.annotation.*; - -/** - */ -@Documented -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.TYPE) -public @interface Module { - - String name() default ""; - - String version() default "1.0"; - - String vendor() default ""; - - String description() default ""; - - License license() default License.UNKNOWN; - - String customLicense() default ""; - - String libraryPath() default "."; - - String override() default ""; - -} diff --git a/libtest/src/main/java/z/offheap/buffer/Buffer.java b/libtest/src/main/java/z/offheap/buffer/Buffer.java deleted file mode 100644 index 1d4ad560f..000000000 --- a/libtest/src/main/java/z/offheap/buffer/Buffer.java +++ /dev/null @@ -1,658 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static z.offheap.buffer.Buffers.*; -import static z.offheap.zmalloc.Allocator.allocate; -import static z.offheap.zmalloc.Allocator.free; -import static z.util.Contracts.contract; - -/** -* Buffer, is one kind off-heap memory storage based on -* {@link z.offheap.zmalloc.Allocator}. -*

-* Note: - *

1. the Buffer instance is not thread safe. - *

2. the buffer storage may contain garbage when created. It is your judgement - * whether to do this clear work via {@link #clear} method. - * -*/ - -public class Buffer implements - NativeOrderBuffer, NetworkOrderBuffer, LittleEndianOrderBuffer { - - protected long capacity; - protected long address; - protected long readCursor; - protected long writeCursor; - - - /** - * WARNING: this constructor is used to support the pre-allocated behavior in - * pool-like scenario. This default constructor just returns an - * uncompleted instance. - *

- * Do not use this constructor unless you know what you are doing. - * Instead, you can use related static factory methods, - * like {@link #create}. - */ - @Deprecated - public Buffer() {} - - protected Buffer(long capacity) { - this.address = allocate(capacity); - this.capacity = capacity; - } - - protected Buffer(long address, long capacity) { - this.address = address; - this.capacity = capacity; - } - - public static final ByteBuffer create(long capacity) { - return new Buffer(capacity); - } - - public static final ByteBuffer create(long address, long capacity) { - return new Buffer(address, capacity); - } - - @Override - public long capacity() { - return capacity; - } - - @Deprecated - public void capacity(long capacity) { - this.capacity = capacity; - } - - @Override - public long address() { - return address; - } - - @Deprecated - public void address(long address) { - this.address = address; - } - - @Override - public final Buffer clear() { - Buffers.clear(address, (int) capacity); - return this; - } - - @Override - public final long readCursor() { - return readCursor; - } - - @Override - public final long writeCursor() { - return writeCursor; - } - - @Override - public final Buffer reset() { - readCursor = 0; - writeCursor = 0; - return this; - } - - @Override - public final boolean isReadable() { - return writeCursor > readCursor; - } - - @Override - public final boolean isReadable(long numBytes) { - return writeCursor - readCursor >= numBytes; - } - - @Override - public final boolean isWritable() { - return capacity > writeCursor; - } - - @Override - public boolean isWritable(long numBytes) { - return capacity - writeCursor >= numBytes; - } - - @Override - public final long readableBytes() { - return writeCursor - readCursor; - } - - @Override - public final long writableBytes() { - return capacity - writeCursor; - } - - - //======================================================================= - //read primitives - - @Override - public final byte read() { - contractToRead(1); - byte v = get(address + readCursor); - readCursor++; - return v; - } - - @Override - public final short readShort() { - contractToRead(2); - short v = getShort(address + readCursor); - readCursor += 2; - return v; - } - - @Override - public final int readInt() { - contractToRead(4); - int v = getInt(address + readCursor); - readCursor += 4; - return v; - } - - @Override - public final long readLong() { - contractToRead(8); - long v = getLong(address + readCursor); - readCursor += 8; - return v; - } - - @Override - public final char readChar() { - return (char)readShort(); - } - - @Override - public final float readFloat() { - contractToRead(4); - float v = getFloat(address + readCursor); - readCursor += 4; - return v; - } - - @Override - public final double readDouble() { - contractToRead(8); - double v = getDouble(address + readCursor); - readCursor += 8; - return v; - } - - //====================================================================== - //NetworkOrder read - @Override - public final short readShortN() { - contractToRead(2); - short v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getShort(address + readCursor) : getShortNonNative(address + readCursor); - readCursor += 2; - return v; - } - - @Override - public final int readIntN() { - contractToRead(4); - int v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getInt(address + readCursor) : getIntNonNative(address + readCursor); - readCursor += 4; - return v; - } - - @Override - public final long readLongN() { - contractToRead(8); - long v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getLong(address + readCursor) : getLongNonNative(address + readCursor); - readCursor += 8; - return v; - } - - @Override - public final char readCharN() { - return NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - (char)readShort() : (char) readShortN(); - } - - @Override - public final float readFloatN() { - contractToRead(4); - float v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getFloat(address+readCursor) : getFloatNonNative(address+readCursor); - readCursor += 4; - return v; - } - - @Override - public final double readDoubleN() { - contractToRead(8); - double v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getDouble(address+readCursor) : getDoubleNonNative(address+readCursor); - readCursor += 8; - return v; - } - - //====================================================================== - //LittleEndian read - @Override - public final short readShortLE() { - contractToRead(2); - short v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getShortNonNative(address+readCursor) : getShort(address+readCursor); - readCursor += 2; - return v; - } - - @Override - public final int readIntLE() { - contractToRead(4); - int v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getIntNonNative(address + readCursor) : getInt(address + readCursor) ; - readCursor += 4; - return v; - } - - @Override - public final long readLongLE() { - contractToRead(8); - long v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getLongNonNative(address + readCursor) : getLong(address + readCursor); - readCursor += 8; - return v; - } - - @Override - public final char readCharLE() { - return NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - (char)readShortLE() : (char)readShort(); - } - - @Override - public final float readFloatLE() { - contractToRead(4); - float v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getFloatNonNative(address+readCursor) : getFloat(address+readCursor); - readCursor += 4; - return v; - } - - @Override - public final double readDoubleLE() { - contractToRead(8); - double v = NATIVE_ORDER_SAME_TO_NETWORK_ORDER ? - getDoubleNonNative(address+readCursor) : getDouble(address+readCursor); - readCursor += 8; - return v; - } - - /** - * - * WARNING: make sure you understand the bytes that you want to skip may - * contain garbage, in that {@link z.offheap.buffer.Buffer} does not - * clear the allocated memory area when created. - */ - @Override - public final void skipRead(long length) { - contractReadCursor(readCursor+length); - readCursor += length; - } - - /** - * - * WARNING: make sure you understand the bytes that you want to skip may - * contain garbage, in that {@link z.offheap.buffer.Buffer} does not - * clear the allocated memory area when created. - */ - @Override - public final void skipReadTo(long index) { - contractReadCursor(index); - readCursor = index; - } - - @Override - public final void readTo(ByteBuffer dstbuffer, long length) { - copy(address+readCursor, - dstbuffer.address()+dstbuffer.writeCursor(), - length); - dstbuffer.skipWrite(length); - } - - //====================================================================== - //write primitives - - @Override - public final Buffer write(byte value) { - contractToWrite(1); - put(address + writeCursor, value); - writeCursor++; - return this; - } - - @Override - public final Buffer writeShort(short value) { - contractToWrite(2); - putShort(address + writeCursor, value); - writeCursor += 2; - return this; - } - - @Override - public final Buffer writeInt(int value) { - contractToWrite(4); - putInt(address + writeCursor, value); - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeLong(long value) { - contractToWrite(8); - putLong(address + writeCursor, value); - writeCursor += 8; - return this; - } - - @Override - public final Buffer writeChar(char value) { - writeShort((short) value); - return this; - } - - @Override - public final Buffer writeFloat(float value) { - contractToWrite(4); - putFloat(address + writeCursor, value); - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeDouble(double value) { - contractToWrite(8); - putDouble(address + writeCursor, value); - writeCursor += 8; - return this; - } - - //======================================================================= - //NetworkOrder write - @Override - public final Buffer writeShortN(short value) { - contractToWrite(2); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putShort(address + writeCursor, value); - }else { - putShortNonNative(address + writeCursor, value); - } - writeCursor += 2; - return this; - } - - @Override - public final Buffer writeIntN(int value) { - contractToWrite(4); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putInt(address + writeCursor, value); - } else { - putIntNonNative(address + writeCursor, value); - } - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeLongN(long value) { - contractToWrite(8); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putLong(address + writeCursor, value); - } else { - putLongNonNative(address + writeCursor, value); - } - writeCursor += 8; - return this; - } - - @Override - public final Buffer writeCharN(char value) { - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - writeShort((short) value); - } else { - writeShortN((short) value); - } - return this; - } - - @Override - public final Buffer writeFloatN(float value) { - contractToWrite(4); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putFloat(address + writeCursor, value); - } else { - putFloatNonNative(address + writeCursor, value); - } - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeDoubleN(double value) { - contractToWrite(8); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putDouble(address + writeCursor, value); - } else { - putDoubleNonNative(address + writeCursor, value); - } - writeCursor += 8; - return this; - } - - //======================================================================= - //LittleEndianOrder write - @Override - public final Buffer writeShortLE(short value) { - contractToWrite(2); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putShortNonNative(address + writeCursor, value); - }else { - putShort(address + writeCursor, value); - } - writeCursor += 2; - return this; - } - - @Override - public final Buffer writeIntLE(int value) { - contractToWrite(4); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putIntNonNative(address + writeCursor, value); - } else { - putInt(address + writeCursor, value); - } - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeLongLE(long value) { - contractToWrite(8); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putLongNonNative(address + writeCursor, value); - } else { - putLong(address + writeCursor, value); - } - writeCursor += 8; - return this; - } - - @Override - public final Buffer writeCharLE(char value) { - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - writeShortLE((short) value); - } else { - writeShort((short) value); - } - return this; - } - - @Override - public final Buffer writeFloatLE(float value) { - contractToWrite(4); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putFloatNonNative(address + writeCursor, value); - } else { - putFloat(address + writeCursor, value); - } - writeCursor += 4; - return this; - } - - @Override - public final Buffer writeDoubleLE(double value) { - contractToWrite(8); - if (NATIVE_ORDER_SAME_TO_NETWORK_ORDER) { - putDoubleNonNative(address + writeCursor, value); - } else { - putDouble(address + writeCursor, value); - } - writeCursor += 8; - return this; - } - - /** - * - * WARNING: make sure you understand the bytes that you want to skip may - * contain garbage, in that {@link z.offheap.buffer.Buffer} does not - * clear the allocated memory area when created. - */ - @Override - @Deprecated - public final Buffer skipWrite(long length) { - contractWriteCursor(writeCursor+length); - writeCursor += length; - return this; - } - - /** - * - * WARNING: make sure you understand the bytes that you want to skip may - * contain garbage, in that {@link z.offheap.buffer.Buffer} does not - * clear the allocated memory area when created. - */ - @Override - @Deprecated - public final Buffer skipWriteTo(long index) { - contractWriteCursor(index); - writeCursor = index; - return this; - } - - - //====================================================================== - //common contracts - protected static boolean enable_contracts = true; - - protected final void contractToWrite(long nBytes) { - if (enable_contracts) - contract(()-> (address!=0) && isWritable(nBytes) ); - } - - protected final void contractToRead(long nBytes) { - if (enable_contracts) - contract(()-> (address!=0) && isReadable(nBytes) ); - } - - protected final void contractReadCursor(long readCursor) { - if (enable_contracts) - contract(()-> - (address!=0) && (readCursor > 0) && (readCursor < writeCursor) ); - } - - protected final void contractWriteCursor(long writeCursor) { - if (enable_contracts) - contract(()-> - (address!=0) && (writeCursor > 0) && (writeCursor < capacity) ); - } - - public static final void disableContracts() { - enable_contracts = false; - } - - - //====================================================================== - @Override - public final NativeOrderBuffer nativeOrder() { - return this; - } - - @Override - public final NetworkOrderBuffer networkOrder() { - return this; - } - - @Override - public final LittleEndianOrderBuffer littleEndianOrder() { - return this; - } - - //====================================================================== - //clean-up - - @Override - public final void close() { - if (address!=0) { - free(address); - address=0; - } - } - - @Override - public final void finalize() { - close(); - } - -} - diff --git a/libtest/src/main/java/z/offheap/buffer/BufferStateException.java b/libtest/src/main/java/z/offheap/buffer/BufferStateException.java deleted file mode 100644 index a24287373..000000000 --- a/libtest/src/main/java/z/offheap/buffer/BufferStateException.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - */ -public class BufferStateException extends IllegalStateException { - public BufferStateException() { - } - - public BufferStateException(String s) { - super(s); - } - - public BufferStateException(String message, Throwable cause) { - super(message, cause); - } - - public BufferStateException(Throwable cause) { - super(cause); - } -} diff --git a/libtest/src/main/java/z/offheap/buffer/Buffers.java b/libtest/src/main/java/z/offheap/buffer/Buffers.java deleted file mode 100644 index ce8213d7d..000000000 --- a/libtest/src/main/java/z/offheap/buffer/Buffers.java +++ /dev/null @@ -1,335 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.nio.ByteOrder; - -import static z.util.Unsafes.UNSAFE; - -/** - */ -public final class Buffers { - - public static final boolean NATIVE_ORDER_SAME_TO_NETWORK_ORDER = - (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN); - - //===================================================================== - //primitives get helpers - - public static final byte get(long address) { - return UNSAFE.getByte(address); - } - - public static final int getInt(long address) { - return UNSAFE.getInt(address); - } - - public static final int getIntNonNative(long address) { - return Integer.reverseBytes(UNSAFE.getInt(address)); - } - - public static final long getLong(long address) { - return UNSAFE.getLong(address); - } - - public static final long getLongNonNative(long address) { - return Long.reverseBytes(UNSAFE.getLong(address)); - } - - public static final short getShort(long address){ - return UNSAFE.getShort(address); - } - - public static final short getShortNonNative(long address){ - return Short.reverseBytes(UNSAFE.getShort(address)); - } - - public static final float getFloat(long address) { - return UNSAFE.getFloat(address); - } - - public static final float getFloatNonNative(long address) { - return Float.intBitsToFloat(getIntNonNative(address)); - } - - public static final double getDouble(long address) { - return UNSAFE.getDouble(address); - } - - public static final double getDoubleNonNative(long address) { - return Double.longBitsToDouble(getLongNonNative(address)); - } - - public static final long getAddress(long address) { - return UNSAFE.getAddress(address); - } - - //===================================================================== - //primitives put helpers - - public static final void put(long address, byte value) { - UNSAFE.putByte(address,value); - } - - public static final void putInt(long address, int value) { - UNSAFE.putInt(address,value); - } - - public static final void putIntNonNative(long address, int value) { - UNSAFE.putInt(address, Integer.reverseBytes(value)); - } - - public static final void putLong(long address, long value) { - UNSAFE.putLong(address, value); - } - - public static final void putLongNonNative(long address, long value) { - UNSAFE.putLong(address, Long.reverseBytes(value)); - } - - public static final void putShort(long address, short value) { - UNSAFE.putShort(address, value); - } - - public static final void putShortNonNative(long address, short value) { - UNSAFE.putShort(address, Short.reverseBytes(value)); - } - - public static final void putFloat(long address, float value) { - UNSAFE.putFloat(address, value); - } - - public static final void putFloatNonNative(long address, float value) { - putIntNonNative(address, Float.floatToRawIntBits(value)); - } - - public static final void putDouble(long address, double value) { - UNSAFE.putDouble(address, value); - } - - public static final void putDoubleNonNative(long address, double value) { - putLongNonNative(address, Double.doubleToRawLongBits(value)); - } - - public static final void putAddress(long address,long addrValue) { - UNSAFE.putAddress(address,addrValue); - } - - //===================================================================== - //unsign primitive helpers - - public static final int toUnsignedByte(byte value) { - return value & 0xFF; - } - - public static final int toUnsignedShort(short value) { - return value & 0xFFFF; - } - - public static final long toUnsignedInt(int value) { - return value & 0xFFFFFFFFL; - } - - //===================================================================== - //boolean primitive helpers - - public static final boolean toBoolean(byte value) { - return value==0 ? false:true; - } - - public static final boolean toBoolean(int value) { - return value==0 ? false:true; - } - - public static final boolean toBoolean(long value) { - return value==0 ? false:true; - } - - public static final byte byteFromBoolean(boolean b) { - return b==false ? 0: (byte)1; - } - - public static final int intFromBoolean(boolean b) { - return b==false ? 0: 1; - } - - public static final long longFromBoolean(boolean b) { - return b==false ? 0: 1L; - } - - //===================================================================== - //system helpers - - //wow.... - public static final void clear(long address, int length) { -// contract(() -> length > 0); - - switch (length) { - case 1 : - UNSAFE.putByte(address, (byte)0); - break; - case 2: - UNSAFE.putShort(address, (short)0); - break; - case 3: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putShort(address+1, (short)0); - break; - case 4: - UNSAFE.putInt(address, 0); - break; - case 5: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putInt(address+1, 0); - break; - case 6: - UNSAFE.putShort(address, (short) 0); - UNSAFE.putInt(address+2, 0); - break; - case 7: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putShort(address+1, (short)0); - UNSAFE.putInt(address+3, 0); - break; - case 8: - UNSAFE.putLong(address, 0); - break; - case 9: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putLong(address+1, 0); - break; - case 10: - UNSAFE.putShort(address, (short)0); - UNSAFE.putLong(address+2, 0); - break; - case 11: - UNSAFE.putByte(address, (byte) 0); - UNSAFE.putShort(address+1, (short)0); - UNSAFE.putLong(address+3, 0); - break; - case 12: - UNSAFE.putInt(address, 0); - UNSAFE.putLong(address+4, 0); - break; - case 13: - UNSAFE.putByte(address, (byte) 0); - UNSAFE.putInt(address+1, 0); - UNSAFE.putLong(address+5, 0); - break; - case 14: - UNSAFE.putShort(address, (short) 0); - UNSAFE.putInt(address+2, 0); - UNSAFE.putLong(address+6, 0); - break; - case 15: - UNSAFE.putByte(address, (byte) 0); - UNSAFE.putShort(address+1, (short) 0); - UNSAFE.putInt(address+3, 0); - UNSAFE.putLong(address+7, 0); - break; - case 16: - UNSAFE.putLong(address, 0); - UNSAFE.putLong(address+8,0); - break; - case 17: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putLong(address+1, 0); - UNSAFE.putLong(address+9, 0); - break; - case 18: - UNSAFE.putShort(address, (short)0); - UNSAFE.putLong(address+2, 0); - UNSAFE.putLong(address+10,0); - break; - case 19: - UNSAFE.putByte(address, (byte)0); - UNSAFE.putShort(address+1, (short)0); - UNSAFE.putLong(address+3, 0); - UNSAFE.putLong(address+11,0); - break; - case 32: - UNSAFE.putLong(address,0); - UNSAFE.putLong(address+8,0); - UNSAFE.putLong(address+16,0); - UNSAFE.putLong(address+24,0); - break; - default: - UNSAFE.setMemory(address, length, (byte)0); - } - } - - public static final void clearLong(long address, long length) { - UNSAFE.setMemory(address, length, (byte)0); - } - - public static final void fill(long address, int length, byte value) { -// contract(() -> length > 0); - - switch (length) { - case 1 : - UNSAFE.putByte(address, value); - break; - case 2: - UNSAFE.putByte(address, value); - UNSAFE.putByte(address+1, value); - break; - case 3: - UNSAFE.putByte(address, value); - UNSAFE.putByte(address+1, value); - UNSAFE.putByte(address+2, value); - break; - case 4: - UNSAFE.putByte(address, value); - UNSAFE.putByte(address+1, value); - UNSAFE.putByte(address+2, value); - UNSAFE.putByte(address+3, value); - break; - default: - UNSAFE.setMemory(address, length, value); - } - - } - - public static final void copy( - long srcStartAddress, long dstStartAddress, long length) { - UNSAFE.copyMemory(srcStartAddress,dstStartAddress,length); - } - - -} diff --git a/libtest/src/main/java/z/offheap/buffer/ByteBuffer.java b/libtest/src/main/java/z/offheap/buffer/ByteBuffer.java deleted file mode 100644 index 5076cc18d..000000000 --- a/libtest/src/main/java/z/offheap/buffer/ByteBuffer.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Created by jin on 12/27/13. - */ -public interface ByteBuffer extends AutoCloseable { - - /** - * Returns this buffer's capacity. - * - * @return The capacity of this buffer - */ - long capacity(); - - long address(); - - ByteBuffer clear(); - - long readCursor(); - - long writeCursor(); - - ByteBuffer reset(); - - boolean isReadable(); - - boolean isReadable(long numBytes); - - boolean isWritable(); - - boolean isWritable(long numBytes); - - long readableBytes(); - - long writableBytes(); - - byte read(); - - void skipRead(long length); - - void skipReadTo(long index); - - void readTo(ByteBuffer dstbuffer, long length); - - //FIXME: override to byte[] and nio buffer - - ByteBuffer write(byte value); - - NativeOrderBuffer nativeOrder(); - - NetworkOrderBuffer networkOrder(); - - LittleEndianOrderBuffer littleEndianOrder(); - - @Deprecated - ByteBuffer skipWrite(long length); - - @Deprecated - ByteBuffer skipWriteTo(long index); - - void close(); - -// @Override -// void finalize(); -} diff --git a/libtest/src/main/java/z/offheap/buffer/LittleEndianOrderBuffer.java b/libtest/src/main/java/z/offheap/buffer/LittleEndianOrderBuffer.java deleted file mode 100644 index 65b2296c1..000000000 --- a/libtest/src/main/java/z/offheap/buffer/LittleEndianOrderBuffer.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - */ -public interface LittleEndianOrderBuffer extends ByteBuffer { - short readShortLE(); - - int readIntLE(); - - long readLongLE(); - - char readCharLE(); - - float readFloatLE(); - - double readDoubleLE(); - - LittleEndianOrderBuffer writeShortLE(short value); - - LittleEndianOrderBuffer writeIntLE(int value); - - LittleEndianOrderBuffer writeLongLE(long value); - - LittleEndianOrderBuffer writeCharLE(char value); - - LittleEndianOrderBuffer writeFloatLE(float value); - - LittleEndianOrderBuffer writeDoubleLE(double value); - - LittleEndianOrderBuffer write(byte value); -} diff --git a/libtest/src/main/java/z/offheap/buffer/NativeOrderBuffer.java b/libtest/src/main/java/z/offheap/buffer/NativeOrderBuffer.java deleted file mode 100644 index 0a78b24a2..000000000 --- a/libtest/src/main/java/z/offheap/buffer/NativeOrderBuffer.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - */ -public interface NativeOrderBuffer extends ByteBuffer { - short readShort(); - - int readInt(); - - long readLong(); - - char readChar(); - - float readFloat(); - - double readDouble(); - - Buffer writeShort(short value); - - Buffer writeInt(int value); - - Buffer writeLong(long value); - - Buffer writeChar(char value); - - Buffer writeFloat(float value); - - Buffer writeDouble(double value); - - NativeOrderBuffer write(byte value); -} diff --git a/libtest/src/main/java/z/offheap/buffer/NetworkOrderBuffer.java b/libtest/src/main/java/z/offheap/buffer/NetworkOrderBuffer.java deleted file mode 100644 index 8bec849f6..000000000 --- a/libtest/src/main/java/z/offheap/buffer/NetworkOrderBuffer.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.buffer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - */ -public interface NetworkOrderBuffer extends ByteBuffer { - short readShortN(); - - int readIntN(); - - long readLongN(); - - char readCharN(); - - float readFloatN(); - - double readDoubleN(); - - NetworkOrderBuffer writeShortN(short value); - - NetworkOrderBuffer writeIntN(int value); - - NetworkOrderBuffer writeLongN(long value); - - NetworkOrderBuffer writeCharN(char value); - - NetworkOrderBuffer writeFloatN(float value); - - NetworkOrderBuffer writeDoubleN(double value); - - NetworkOrderBuffer write(byte value); -} diff --git a/libtest/src/main/java/z/offheap/zmalloc/Allocator.java b/libtest/src/main/java/z/offheap/zmalloc/Allocator.java deleted file mode 100644 index e490e4b79..000000000 --- a/libtest/src/main/java/z/offheap/zmalloc/Allocator.java +++ /dev/null @@ -1,1176 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.offheap.zmalloc; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import z.channel.MPMCQueue; -import z.util.SystemProperty; -import z.util.primitives.Longs; - -import java.util.Optional; - -import static z.util.Contracts.contract; -import static z.util.Unsafes.*; - -/** - * "zmalloc" - Landz's off-heap direct memory allocator. - *

- * It is designed to have high performance with scalable hardware support, - * zero garbage generation and built-in statistics in the pure Java. - * - *

- * zmalloc concepts: - *

GlobalPool: a memory area which the allocator can use. - *

ThreadLocalPool: a thread local memory area for efficient chunk - * allocation. - * It will request/release memories from/to GlobalPool - * when necessary. - *

Page: a up-level internal allocation unit which is larger than a Chunk, - * as the chunk cache for some specific SizeType. - * it is always in 3 states: free, partial, full. - *

Chunk: just a continuous memory block, basic allocation unit in zmalloc, - * which is in a specific SizeType - *

SizeClass: Chunks are only allocated in one size of SizeClass - * - *

- * - * Designer Note: - * - *

- * Landz's zmalloc is designed from scratch to avoid any license problem. - * There are three public reference for the inspirations: - *

    - *
  • one article about Memcached's slab allocator - * (http://nosql.mypopescu.com/post/13506116892/memcached-internals-memory- - * allocation-eviction); - *
  • one book chapter about linux kernel slab allocator(detailed link); - *
  • one paper about lock-free memory allocator by Oracle's Dave Dice - * (detailed link); - *
- *

- * Generally, the zmalloc is more like as slab allocator. - * It has the possible to meet "pathological case" - * (There are workaround suggestions). But as my investigate to public bug - * reports, ptmalloc and tcmalloc also has this problem. - * - *

- * The main structure of current impl is two-level: ZMPage, and ZMChunck. - * Because the page and chunk is common, so I ZM- prefix to make them - * distinguished with others. And more additions, the naming in Allocator class - * is C-style, in that:
- *

    - *
  • there are many new concepts in this allocator field, - * then full camel-case naming is hard to read, short camel-case naming is - * talking nothing;
    - *
  • to warning the developer we are in dangerous area. - *
- *

- * ZMPage: is the container of the ZMChuncks, which is 2MB now. - * 2MB is not random chosen size, which is the default large Page size in - * Linux. This limits current max size of chunk can be allocated by zmalloc - *
(NOTE: There are several ways for solving this. I may push a version to - * support a larger size in a near future.) - * - *

- * Because zmalloc makes the page to the same size. The page can be shared - * between threadlocal pool and global pool. - * This is different to the size of slab of Memcached as my understanding to - * that reference. (I don't comment on which is better in that the references - * can not drive us to any conclusion.) - * - *

- * ZMChunck is the final allocated memory area for use. Once a ZMPage is used - * to allocate ZMChuncks, then ZMPage is assigned to a sizeClass which indicated - * this ZMPage is for which size of ZMChunck. A sizeClassIndex(or sci for short - * in sources) is for indexing one size of the pre-allocate table(or said a - * continuous memory area in the offheap area). This is just for speeding and - * improving memory locality(all this can be done by on-heap objects). - * - *

- * ZMPage contains the metadata of ZMChunk. This is different to that the - * jemalloc uses RBTree. (I don't comment on which is better in that the - * references can not drive us to any conclusion.) - * - * One design tip with the ZMPage is that, the ZMPage is 2MB aligned in design, - * it make to reason one ZMChunk belongs to which ZMPage in two of fastest - * instructions in all of CPUs): - * - *

- * long addressPage = addressChunk-(addressChunk&(SIZE_ZMPAGE-1));
- * 
- * - * see this statement in {@link Allocator#free} - * - *

- * This gives big benefits: ZMChunk doesn't need to store any metadata. This - * shortens the critical path and decide how the way landz manage the metadata. - * - *

- * ZMPages are from a global pages pool, then a ZMPage should be in a thread - * pool to use. When ZMPage in the free state, it is can be freed to global pool - * when some conditions. - * - *

- * The operations inside of threadLocal pool, as the name indicated, are thread safe. - * - *

- * The Operations from threadLocal pool to global pool use the landz's - * {@link MPMCQueue}, which provide a high-throughput/low-latency lock-free - * bounded queue. - * - *

- * One important side of thread-safe design to allocator is the cross-thread - * free invocation. ZMalloc solves this like this: - *

    - *
  • metadata records the thread's tid when allocated, call ownedTid; - *
  • if free thread's tid found not match with ownedTid, then zmalloc just - * put the return that chunk to ownedTid's remote freed queue. - *
  • when some conditions happens, the remote freed queue will be drained for - * use; - *
  • remote freed queue is a careful design lock-free/ABA-free MPSC queue - * by Landz itself; - *
- * - *

- * more detail comes here... - * - *

- * Namings in the methods: - *

    - *
  • pg_ is for page operations; - *
  • gp_ is for global pool operations; - *
  • tlp_ is for threadlocal pool operations; - * - *
- * - * - */ -public class Allocator { - - /** - * Note: most of variables are stored in the long type for future's extensions - * and/or 8B alignment, but we may only use the int/byte-size slot when - * long-size is not needed. - */ - - private static final long NULL_ADDRESS = 0L; - -// private static final int SIZE_LONG_TYPE = 8; -// private static final int SIZE_INT_TYPE = 4; - - /** - * addressZM: zmalloc metadata area + global pool area - * arrange 8MB VM metadata area for zmalloc globals, may be - * reallocated in the future; - */ - private static final long addressRaw; - private static final long addressZM; - - private static final long SIZE_METADATA_ZMALLOC = 1<<23; - - - /** - * 35 kinds of SizeClass now - */ - private static final long addressSizeClassInfo; - private static final int TLPHEAD_TO_SIZECLASSINFO = 4*1024; - - /** - * off-heap ThreadLocalPools area desgin is an optimization for - * java ThreadLocal. It provide a better TLS implementation for zmalloc - * than ThreadLocal in long-running thread usage, like thread pool. - * - * But this design can not support that applications like to spawn huge mounts - * of short-living thread. Becuase threads can be generated infinitely, but - * off-heap is finally limited. - * - * By default, the first 3k-1 thread (id<3072) can have a TLP slot in the - * managed off heap. For the later spawning threads, it will go back to use - * slower {@link ThreadLocal}. - * - * TODO: j.l.ThreadLocal backed TLP schema has not implemented now... - * - * addressTLPs: address for ThreadLocalPools - * structure: - * (.. CacheLine aligned) - * long - number of TLPs - * [..to nextCacheline start] - * [..to nextCacheline start] - * TLPStates - * (note: this area has one-shot false sharing for long-running threads) - * byte - 0.. - * byte - 1.. - * byte - 2.. - * byte - .. - * byte - 3071.. - * [..to next 4096 offset start] - * (addressTLP#0) - * address[36] - AvailablePages - * - AvailablePage[0] - 3*8B: head, tail, num - * .. - * - AvailablePage[35] - * address - freePagesHead - * address - freePagesTail - * long - NumFreePages - * [..to 1024 offset start] - * address - remoteFreedChunksHead - * [..to nextCacheline start] - * [..to nextCacheline start] - * address - remoteFreedChunksTail - * [..to nextCacheline start] - * [..to nextCacheline start] - * address - remoteFreedChunksDummy - * [..to nextCacheline start] - * [..to nextCacheline start] - * - * address[] - batchRequestedPages (1-?) TODO: not used - * the TLP can request pages from GP in batch for performance. - * here, batchRequestedPages is for storing the returned batch - * requested pages. Now we support to 1 to ? varied number of - * pages in one TLP. It is possible to support larger number of - * pages after enlarging the TLP metadata area. - * attention: this area is *transient*, it is the related APIs' - * clients' responsibility to use this at its immediate - * availability. - * - * [..padding to 2048] - * (addressTLP#1) - * .. - */ - private static final long addressTLPHEAD; - private static final int TLPHEAD_NUMTLPS_OFFSET = 0; - - private static final int TLPSTATES_TO_TLPHEAD_OFFSET = SIZE_CACHE_LINE_PADDING; - - private static final long addressTLPStates; - private static final int SIZE_TLPStates_ARRAY_LENGTH = 3*1024; - - private static final int TLPS_TO_TLPHEAD_OFFSET = 4*1024; - - private static final long addressTLPs; - private static final int TLP_ITEM_SIZE = 2*1024; - - - private static final int TLP_AVAILABLEPAGES_OFFSET = 0; - private static final int TLP_AVAILABLEPAGES_ITEM_SIZE = 3*SIZE_LONG_TYPE; - private static final int TLP_AVAILABLEPAGES_ARRAYLENGTH = 36; - - private static final int TLP_FREEPAGESHEAD_OFFSET = - TLP_AVAILABLEPAGES_ITEM_SIZE * TLP_AVAILABLEPAGES_ARRAYLENGTH; - private static final int TLP_FREEPAGESTAIL_OFFSET = - TLP_FREEPAGESHEAD_OFFSET + SIZE_LONG_TYPE; - private static final int TLP_NUMFREEPAGES_OFFSET = - TLP_FREEPAGESTAIL_OFFSET + SIZE_LONG_TYPE; - - private static final int TLP_REMOTEFREEDCHUNKS_HEAD_OFFSET = 1024; - private static final int TLP_REMOTEFREEDCHUNKS_TAIL_OFFSET = - TLP_REMOTEFREEDCHUNKS_HEAD_OFFSET + SIZE_CACHE_LINE_PADDING; - private static final int TLP_REMOTEFREEDCHUNKS_DUMMY_OFFSET = - TLP_REMOTEFREEDCHUNKS_TAIL_OFFSET + SIZE_CACHE_LINE_PADDING; - - private static final int TLP_BATCHREQUESTEDPAGES_OFFSET = - TLP_REMOTEFREEDCHUNKS_DUMMY_OFFSET + SIZE_CACHE_LINE_PADDING; - - - /** - * change this threshold value larger if you want more aggressive cache - */ - private static int freePagesNumThreshold; - /** - * change this return value smaller if you want more aggressive cache - */ - private static int freePagesNumToReturn; - - private static final int FREEPAGES_NUM_THRESHOLD_DEFAULT = 64; - private static final int FREEPAGES_NUM_TORETURN_DEFAULT = 32; - - //TODO: only use this optimization after enabling an configuration option - //Note: this can reduce the request cost more, but cause much initial - // memory footprint - private static final int NUM_BATCHREQUESTEDPAGES_DEFAULT = 4;//or 8? 16? - /** - * (..assumed Cacheline aligned for fist page) - * structure: - * address - availableChunks - * long - numAvailableChunks - * address - nextPage - * address - prevPage - * address - nextFreePage - * address - prevFreePage - * long - numMaxChunks - * int - sizeClass(int) - * int - tid(long) - * (...padding to next cache line) - */ - private static final long SHIFT_SIZE_ZMPAGE = 21; - private static final long SIZE_ZMPAGE = 1< {@link #ZMPAGE_MAX_CHUNK_SIZE} is not supported - */ - private static final int ZMPAGE_MAX_CHUNK_SIZE = - (int) SIZE_ZMPAGE-ZMPAGE_RAWCHUNK_OFFSET; - - /** - * 1GB VM or System for initial off heap pool, can grow into - * maximum 50% of total memory - *

- * - * This adjusting could be avoided by fixing the #ZMALLOC_INITIAL_POOLSIZE - * same to the #ZMALLOC_MAX_POOLSIZE - * - *

- * - * NOTE: - * 1. 1GB is just for VM, not physical memory, but set your System - * Property for avoiding kinds of problems for small servers. - * 2. we make the "effective" address of Global Pool(not including - * the metadata) page-aligned for kinds of goods. - * 3. only to access your requested memory area, otherwise you may - * crash JVM! - * - * structure: - * long - addressAvailablePages - * long - NumAvailablePages - * - * - */ - private static final long addressGP; - - //TODO: add a dynamic change method?(note this may cost too long time or fail) - private static final long sizeGP; - private static final long totalAvailablepages; - - //we use the last 1-cacheline-padded slots before GP for GP's meta head - private static final int GPHEAD_OFFSET = -1*SIZE_CACHE_LINE_PADDING; - -// private static final long addressGPHead; - private static final long addressGPHead_NumAvailablePages; - - private static final MPMCQueue globalPool; - - - static { - //config kinds of options - freePagesNumThreshold = Integer.parseInt( - Optional - .ofNullable(SystemProperty.ZMALLOC_FREEPAGES_NUM_THRESHOLD.value()) - .orElse(String.valueOf(FREEPAGES_NUM_THRESHOLD_DEFAULT))); - - freePagesNumToReturn = Integer.parseInt( - Optional - .ofNullable(SystemProperty.ZMALLOC_FREEPAGES_NUM_TORETURN.value()) - .orElse(String.valueOf(FREEPAGES_NUM_TORETURN_DEFAULT))); - - - long initialGPSize = Long.parseLong( - Optional - .ofNullable(SystemProperty.ZMALLOC_INITIAL_POOLSIZE.value()) - .orElse("1024")); - - contract( - () -> Longs.isPowerOfTwo(initialGPSize), - () -> new IllegalArgumentException("Now the size of global pool " + - "is supported to be the power of 2 only."));//FIXME - - sizeGP = initialGPSize * 1024 * 1024;//initialGPSize is in MB - totalAvailablepages = sizeGP/SIZE_ZMPAGE; - - /* TECH NOTE: - * the malloc-ed VM will be included a metadata head (at least for linux - * glibc's). For mmap-ed block(so-called large chunk), - * this head's size is 2*SIZE_SZ, 16 in x86-64, or 8 in x86. - * So, the glibc's malloc allocated memory is not page aligned in default. - */ - addressRaw = systemAllocateMemory( - SIZE_METADATA_ZMALLOC - + sizeGP - + SIZE_ZMPAGE); //should align to zmalloc page - addressZM = nextZMPageAlignedAddress(addressRaw); - contract(()-> isZMPageAligned(addressZM)); - - /** - * NOTE: - * 1. 8B aligned; - * 2. read-only - */ - addressSizeClassInfo = addressZM; - onAddress(addressSizeClassInfo) - .put(8) // 0 - .followBy(16) // 1 - .followBy(24) // 2 - .followBy(32) // 3 - .followBy(48) // 4 - .followBy(64) // 5 - .followBy(96) // 6 - .followBy(128) // 7 - .followBy(192) // 8 - .followBy(256) // 9 - .followBy(384) // 10 - .followBy(512) // 11 - .followBy(768) // 12 - .followBy(1024) // 13 - .followBy(1536) // 14 - .followBy(2048) // 15 - .followBy(3072) // 16 - .followBy(4096) // 17 - .followBy(6144) // 18 - .followBy(8192) // 19 - .followBy(12288) // 20 - .followBy(16384) // 21 - .followBy(24576) // 22 - .followBy(32768) // 23 - .followBy(49152) // 24 - .followBy(65536) // 25 - .followBy(98304) // 26 - .followBy(131072) // 27 - .followBy(196608) // 28 - .followBy(262144) // 29 - .followBy(393216) // 30 - .followBy(524288) // 31 - .followBy(786432) // 32 - .followBy(1048576) // 33 - .followBy(1572864) // 34 - .followBy(ZMPAGE_MAX_CHUNK_SIZE); // 35 - - addressTLPHEAD = addressSizeClassInfo + TLPHEAD_TO_SIZECLASSINFO; - - // addressTLPHEAD+TLPHEAD_NUMTLPS_OFFSET->addressTLPHEAD, - // in that TLPHEAD_NUMTLPS_OFFSET=0 - onAddress(addressTLPHEAD).put(0);//initial num counter for TLPs - - addressTLPStates = addressTLPHEAD + TLPSTATES_TO_TLPHEAD_OFFSET; - //initialize TLPStates - for (int i = 0; i < SIZE_TLPStates_ARRAY_LENGTH; i++) { - UNSAFE.putByte(addressTLPStates+i,(byte)0); - } - - //only initialize the main thread's ThreadLocalPools - addressTLPs = addressTLPHEAD + TLPS_TO_TLPHEAD_OFFSET; - contract(()-> is4096Aligned(addressTLPs)); - - addressGP = addressZM + SIZE_METADATA_ZMALLOC; - contract(()-> isPageAligned(addressGP)); - - addressGPHead_NumAvailablePages = addressGP + GPHEAD_OFFSET; - - globalPool = new MPMCQueue((int)(initialGPSize/2)); - - - //initialize the GP - gp_ini(); - - //NOTE: this hook will cause buffer finalization panic. - // Although it is paintless, but we can just rely on OS for cleaning. -// Runtime.getRuntime().addShutdownHook( -// new Thread(()->systemFreeMemory(addressRaw))); - - //one-shot fence? - UNSAFE.storeFence(); - } - - - - //============================================================================ - //zmalloc main APIs - - /** - * Note: the allocated memory is not guarantee to be zero-ed. That is. - * it may contain any garbage. - * - *

- * contract: 0 < sizeOfBytes <= 1536k (may change in the future) - *

@param sizeOfBytes - the size which you want to request, in bytes - *

@return - the address of your requested chunk, or NULL_ADDRESS(0L) if - * the allocator can not fulfil your request. - */ - public static final long allocate(long sizeOfBytes) { - int sci = sizeClassIndex((int) sizeOfBytes); - //TODO: add contract here - long tid = currentThreadId(); - - //ensure the tlp area has been initialized - if (UNSAFE.getByte(addressTLPStates + tid) ==0) - tlp_ini(tid); - - //prepare TLP variables - long addressTLP = addressTLPs + TLP_ITEM_SIZE *tid; - long addrAvailablePages = addressTLP + TLP_AVAILABLEPAGES_OFFSET; - long addrAvailablePageHead = addrAvailablePages + - sci* TLP_AVAILABLEPAGES_ITEM_SIZE; - long addrAvailablePageTail = addrAvailablePageHead + SIZE_LONG_TYPE; - long addrNumAvailablePage = addrAvailablePageTail + SIZE_LONG_TYPE; - long numAvailablePage = UNSAFE.getLong(addrNumAvailablePage); - - - long page; - if (numAvailablePage!=0) { - page = UNSAFE.getAddress(addrAvailablePageHead); - - boolean isFreePage = - UNSAFE.getInt(page+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET)== - UNSAFE.getInt(page+ZMPAGE_NUMMAXCHUNKS_OFFSET); -// FIXME: merged with next branch's pop? -// chunk = pg_AvailableChunks_pop(page); - - if(isFreePage) { - //meets free page, but now it is not - long addrFreePagesHead = addressTLP + TLP_FREEPAGESHEAD_OFFSET; - long addrFreePagesTail = addressTLP + TLP_FREEPAGESTAIL_OFFSET; - long addrNumFreePages = addressTLP + TLP_NUMFREEPAGES_OFFSET; - tlp_FreePages_remove( - addrFreePagesHead, addrFreePagesTail, addrNumFreePages, page); - } - } else { - page = gp_Page_poll(); - - //check with RemoteFreedChunks - //TODO: do we have/need more better flush points? - long addrRemoteFreedChunksHead = - addressTLP + TLP_REMOTEFREEDCHUNKS_HEAD_OFFSET; - long addrRemoteFreedChunksTail = - addressTLP + TLP_REMOTEFREEDCHUNKS_TAIL_OFFSET; - long addrRemoteFreedChunksDummy = - addressTLP + TLP_REMOTEFREEDCHUNKS_DUMMY_OFFSET; - if (page == NULL_ADDRESS) { - //find in RemoteFreedChunks - long c; - while (NULL_ADDRESS != (c=tlp_RemoteFreedChunksHead_remove( - addrRemoteFreedChunksHead, - addrRemoteFreedChunksTail, - addrRemoteFreedChunksDummy)) ) { - long p = c-(c&(SIZE_ZMPAGE-1)); - if (sci==UNSAFE.getInt(p + ZMPAGE_SIZECLASSINDEX_OFFSET)) { - return c; - } else { - free(c); - } - } - } else { - //flush whole RemoteFreedChunks queue - long c; - while (NULL_ADDRESS != (c=tlp_RemoteFreedChunksHead_remove( - addrRemoteFreedChunksHead, - addrRemoteFreedChunksTail, - addrRemoteFreedChunksDummy)) ) { - free(c); - } - } - - pg_setupPage(page, sci, tid); - tlp_AvailablePages_addToHead(addrAvailablePageHead, page); - //new page should be a freePage as well -// tlp_FreePages_addToHead(addrFreePagesHead, page, addrNumFreePages); - - } - - //contract: page != NULL_ADDRESS - long chunk = pg_AvailableChunks_pop(page); - - //we guarantee that we have chunks iff we have free pages - //so we should guarantee the invariant: chunk != NULL_ADDRESS - if (UNSAFE.getAddress(page) == NULL_ADDRESS) { - //meets full page(in head), just remove it - tlp_AvailablePages_remove(addrAvailablePageHead, - UNSAFE.getAddress(addrAvailablePageHead)); - } - return chunk; - } - - /** - * contract: - * the want-to-be-freed addressChunk should be allocated by - * {@link z.offheap.zmalloc.Allocator#allocate(long)}. - */ - public static final void free(long addressChunk) { - if (addressChunk<=0) - throw new IllegalArgumentException( - "addressChunk argument can't be less than zero."); - - long tid = currentThreadId(); - long addressPage = addressChunk-(addressChunk&(SIZE_ZMPAGE-1)); - int sci = UNSAFE.getInt(addressPage + ZMPAGE_SIZECLASSINDEX_OFFSET); - int ownedTid = UNSAFE.getInt(addressPage + ZMPAGE_TID_OFFSET); - //the chunk is remote freed - if (tid!=ownedTid) { - long addrRemoteFreedChunksTail = addressTLPs + TLP_ITEM_SIZE *ownedTid - + TLP_REMOTEFREEDCHUNKS_TAIL_OFFSET; - tlp_RemoteFreedChunksHead_add(addrRemoteFreedChunksTail,addressChunk); - return; - } - - boolean isFullPage = - UNSAFE.getInt(addressPage+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET)==0; - - pg_AvailableChunks_push(addressPage,addressChunk); - - if (isFullPage) { - tlp_AvailablePages_addToTail(tid, sci, addressPage); - } - //is free page - if (UNSAFE.getInt(addressPage+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET)== - UNSAFE.getInt(addressPage+ZMPAGE_NUMMAXCHUNKS_OFFSET)) { - //TODO: add an option to configure whether to return freePages to GP? - - long addressTLP = addressTLPs + TLP_ITEM_SIZE *tid; - long addrFreePagesHead = addressTLP + TLP_FREEPAGESHEAD_OFFSET; - long addrNumFreePages = addressTLP + TLP_NUMFREEPAGES_OFFSET; - - tlp_FreePages_addToHead(addrFreePagesHead, addressPage, addrNumFreePages); - - if (UNSAFE.getLong(addrNumFreePages) > freePagesNumThreshold) { - //TODO: add batch return method? - //NOTE: freePages include different sizeClass's pages - //TODO: separate the freePages to different sizeClasses? - long addrFreePagesTail = addrFreePagesHead + SIZE_LONG_TYPE; - - for (int i=0;i< freePagesNumToReturn;i++) { - //remove from head - long head = UNSAFE.getAddress(addrFreePagesHead); - //here contract: fpHead!=NULL_ADDRESS - tlp_FreePages_remove( - addrFreePagesHead, addrFreePagesTail, addrNumFreePages, head); - - long addrAvailablePageHead = addressTLP + TLP_AVAILABLEPAGES_OFFSET + - UNSAFE.getInt(head + ZMPAGE_SIZECLASSINDEX_OFFSET) - * TLP_AVAILABLEPAGES_ITEM_SIZE; - //FIXME: addrAvailablePageHead is sci-based, - // and then tlp_AvailablePages_remove is error-prone - tlp_AvailablePages_remove(addrAvailablePageHead,head); - - gp_Page_offer(head); - } - } - } - - } - - //============================================================================ - //internal operations - - //============================================================================ - //internal utils - private static final long nextZMPageAlignedAddress(long address) { - return address-(address&(SIZE_ZMPAGE-1))+SIZE_ZMPAGE; - } - - private static final boolean isZMPageAligned(long address) { - return (address&(SIZE_ZMPAGE-1))==0; - } - - private static final long next4096AlignedAddress(long address) { - return address-(address&4095)+4096; - } - - private static final boolean is4096Aligned(long address) { - return (address&4095)==0; - } - -// private static int[] sizeClassTable0; -// private static int[] sizeClassTable1; -// private static int[] sizeClassTable2; -// -// /** -// * -// * XXX: although this table based way beats sizeClassIndex0 in micro-bench, -// * it should be tested against real world usage. -// * -// */ -// public static final int sizeClassIndex(int sizeOfBytes) { -// if (sizeOfBytes<=0) -// return 0; -// -// sizeOfBytes--; -// int level0 = sizeOfBytes >>> 9; -// int level1 = sizeOfBytes >>> 15; -// int level2 = sizeOfBytes >>> 21; -// -// if (level0==0) { -// return sizeClassTable0[sizeOfBytes>>>2]; -// } else if(level1==0) { -// return sizeClassTable1[sizeOfBytes>>>8]; -// } else if(level2==0) { -// return sizeClassTable2[sizeOfBytes>>>14]; -// } else { -// return 34; -// } -// } -// -// private static final void createSizeClassLookupTable() { -// sizeClassTable0 = new int[128];//0 - 511 -// sizeClassTable1 = new int[128];//512 - 32k-1 -// sizeClassTable2 = new int[128];//32k - 2M-1 -// -// for (int i = 4; i <= 512; i+=4) { -// sizeClassTable0[(i-1)>>>2] = sizeClassIndex0(i); -// } -// -// for (int i = 256; i <= 32*1024; i+=256) { -// sizeClassTable1[(i-1)>>>8] = sizeClassIndex0(i); -// } -// -// for (int i = 16*1024; i <= 2*1024*1024; i+=(16*1024)) { -// sizeClassTable2[(i-1)>>>14] = sizeClassIndex0(i); -// } -// } - - /** - * NOTE: now sizeOfBytes > {@link #ZMPAGE_MAX_CHUNK_SIZE} is not supported - */ - private static final int sizeClassIndex(int sizeOfBytes) { - //contract(()->sizeOfBytes!=0); - if (sizeOfBytes>1572864) { - //TODO: do we support {@link #ZMPAGE_MAX_CHUNK_SIZE} to public? - return 35; - }else if (sizeOfBytes>16) { - int clg = (63-Long.numberOfLeadingZeros(sizeOfBytes)); - int upSizeClass1 = 1< upSizeClass2) { - return (clg-3)*2+1; - } else { - return (clg-3)*2; - } - } else if (sizeOfBytes>8) { - return 1; - } else { - return 0; - } - } - - //============================================================================ - //zmalloc Page operations - private static final void pg_setupPage(long addressPage, - int sizeClassIndex, - long tid) { - int sizeClass = UNSAFE.getInt(addressSizeClassInfo+ - sizeClassIndex*SIZE_INT_TYPE); - int numMaxAvailableChunks = ZMPAGE_MAX_CHUNK_SIZE/sizeClass; - /* - *reset the availableChunks to NULL ? do not need to reset this field here - * if reset done in tlp_ini - */ -// UNSAFE.putAddress(addressPage+ZMPAGE_NEXTPAGE_OFFSET,0); - UNSAFE.putAddress(addressPage, NULL_ADDRESS); - - long addressRawChunks = addressPage+ZMPAGE_RAWCHUNK_OFFSET; - for (int i = 0; i < numMaxAvailableChunks; i++) { - long chunk = addressRawChunks+i*sizeClass; - pg_AvailableChunks_push(addressPage, chunk); - } - UNSAFE.putInt(addressPage + ZMPAGE_NUMAVAILABLECHUNKS_OFFSET, - numMaxAvailableChunks); - UNSAFE.putInt(addressPage + ZMPAGE_NUMMAXCHUNKS_OFFSET, - numMaxAvailableChunks); - UNSAFE.putInt(addressPage + ZMPAGE_SIZECLASSINDEX_OFFSET, - sizeClassIndex); - //FIXME: tid is long type, but now we only support integer number tid - UNSAFE.putInt(addressPage + ZMPAGE_TID_OFFSET, (int) tid); - } - - private static final void pg_AvailableChunks_push(long addressPage, - long addressChunk) { - //availableChunks = addressPage - long head = UNSAFE.getAddress(addressPage); - UNSAFE.putAddress(addressPage, addressChunk); - UNSAFE.putAddress(addressChunk, head); - - long numAvailableChunks = addressPage+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET; - UNSAFE.putInt(numAvailableChunks, UNSAFE.getInt(numAvailableChunks) + 1); - } - - private static final long pg_AvailableChunks_pop(long addressPage) { - //long availableChunks = addressPage+ZMPAGE_AVAILABLECHUNKS_OFFSET; - long head = UNSAFE.getAddress(addressPage); - if (head != NULL_ADDRESS) { - UNSAFE.putAddress(addressPage, UNSAFE.getAddress(head)); - - long numAvailableChunks = addressPage+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET; - UNSAFE.putInt(numAvailableChunks, UNSAFE.getInt(numAvailableChunks) - 1); - }//meet a full zmalloc page when head ==0L - return head; - } - - //============================================================================ - //GP operations - private static final void gp_ini() { - for (long i = 0; i < totalAvailablepages; i++) { - long availableGPZMPage = addressGP + i*SIZE_ZMPAGE; - gp_Page_offer(availableGPZMPage); - } - } - - /** - * NOTE:

- * the return zmalloc page is raw, it is the responsibility of TLP to - * setup the returned page for its use. - * - * @return the address of raw zmalloc page - */ - private static final long gp_Page_poll() { - long page = globalPool.poll(); - if (page!= MPMCQueue.NULL) { - UNSAFE.getAndAddLong(null, addressGPHead_NumAvailablePages, -1L); - return page; - } else { - return NULL_ADDRESS; - } - } - - private static final void gp_Page_offer(long addressFreePage) { -// int nRetries = 3;//TODO -// for (int i = 0; i < nRetries; i++) { - if (globalPool.offer(addressFreePage)) { - UNSAFE.getAndAddLong(null, addressGPHead_NumAvailablePages, 1L); - return; -// } - } - throw new RuntimeException("can not offer the page to the global pool, " + - "but this should not happen..."); - } - - -// /** -// * TODO: not used -// * NOTE:

-// * in batch, the requested pages will be put into the corresponding -// * batchRequestedPages area. -// * -// */ -// private static final void gp_Page_pop_batch(long tid, -// int numOfRequestedPages) { -// long tlpBatchRequestedPage = addressTLPs+ TLP_ITEM_SIZE *tid -// + TLP_BATCHREQUESTEDPAGES_OFFSET; -// -// long page = UNSAFE.getAddress(addressGPHead_AvailablePagesHead); -// for (int i = 0; i < numOfRequestedPages; i++) { -// if (page==NULL_ADDRESS) -// break; -// UNSAFE.putAddress(tlpBatchRequestedPage + i * SIZE_LONG_TYPE, page); -// page = UNSAFE.getAddress(page); -// } -// UNSAFE.putAddress(addressGPHead_AvailablePagesHead,page); -// -// long newNum = UNSAFE.getLong(addressGPHead_NumAvailablePages)- -// numOfRequestedPages; -// UNSAFE.putLong(addressGPHead_NumAvailablePages, (newNum>0)?newNum:0); -// } - - //============================================================================ - //TLP operations - - private static final void tlp_ini(long tid) { - long addressTLP = addressTLPs + TLP_ITEM_SIZE *tid; - long availablePages = addressTLP; - - for (int i = 0; i < TLP_AVAILABLEPAGES_ARRAYLENGTH; i++) { - long availablePageHead = availablePages + i* TLP_AVAILABLEPAGES_ITEM_SIZE; - long availablePageTail = availablePageHead + SIZE_LONG_TYPE; - - //reset the available pages array - UNSAFE.putAddress(availablePageHead,availablePageTail); - UNSAFE.putAddress(availablePageTail,availablePageHead);//TODO - UNSAFE.putLong(availablePageTail+SIZE_LONG_TYPE, 0); - //XXX: not necessary for numAvailablePage - } - long addrFreePagesHead = addressTLP + TLP_FREEPAGESHEAD_OFFSET; - long addrFreePagesTail = addressTLP + TLP_FREEPAGESTAIL_OFFSET; - UNSAFE.putAddress(addrFreePagesHead,addrFreePagesTail); - UNSAFE.putAddress(addrFreePagesTail,addrFreePagesHead); - //XXX: not necessary for numFreePages - - long addrRemoteFreedChunksHead = - addressTLP + TLP_REMOTEFREEDCHUNKS_HEAD_OFFSET; - long addrRemoteFreedChunksTail = - addressTLP + TLP_REMOTEFREEDCHUNKS_TAIL_OFFSET; - long addrRemoteFreedChunksDummy = - addressTLP + TLP_REMOTEFREEDCHUNKS_DUMMY_OFFSET; - UNSAFE.putAddress(addrRemoteFreedChunksDummy,NULL_ADDRESS);//not necessary - UNSAFE.putAddress(addrRemoteFreedChunksHead,addrRemoteFreedChunksDummy); - UNSAFE.putAddress(addrRemoteFreedChunksTail,addrRemoteFreedChunksDummy); - - UNSAFE.putByte(addressTLPStates + tid, (byte) 1);//set TLPStates - UNSAFE.getAndAddInt(null, addressTLPHEAD, 1); - } - - private static final void tlp_AvailablePages_addToHead( - long addrAvailablePageHead, - long newAvailablePage) { - long addrNumAvailablePage = addrAvailablePageHead+2*SIZE_LONG_TYPE; - long numAvailablePage = UNSAFE.getLong(addrNumAvailablePage); - - long oldHead = UNSAFE.getAddress(addrAvailablePageHead); - - if (numAvailablePage != 0) {//oldHead!=availablePageTail - UNSAFE.putAddress(oldHead + ZMPAGE_PREVPAGE_OFFSET, - newAvailablePage); - }else {//oldHead is tail - UNSAFE.putAddress(oldHead, newAvailablePage); - } - UNSAFE.putAddress(newAvailablePage + ZMPAGE_NEXTPAGE_OFFSET, oldHead); - UNSAFE.putAddress(newAvailablePage + ZMPAGE_PREVPAGE_OFFSET, - addrAvailablePageHead); - UNSAFE.putAddress(addrAvailablePageHead, newAvailablePage); - - UNSAFE.putLong(addrNumAvailablePage, numAvailablePage + 1); - } - - private static final void tlp_AvailablePages_addToTail( - long tid, - int sizeClassIndex, - long newAvailablePage) { - - long addrAvailablePages = addressTLPs + TLP_ITEM_SIZE *tid; -// long availablePageHead = availablePages + -// sizeClassIndex*TLP_AVAILABLEPAGES_ITEM_SIZE; - long addrAvailablePageTail = addrAvailablePages + - sizeClassIndex* TLP_AVAILABLEPAGES_ITEM_SIZE + - SIZE_LONG_TYPE; - long addrNumAvailablePage = addrAvailablePageTail+SIZE_LONG_TYPE; - long numAvailablePage = UNSAFE.getLong(addrNumAvailablePage); - - long oldTail = UNSAFE.getAddress(addrAvailablePageTail); - if (numAvailablePage != 0) {//oldTail is not addrAvailablePageHead - UNSAFE.putAddress(oldTail + ZMPAGE_NEXTPAGE_OFFSET, - newAvailablePage); - } else {//oldTail is head - UNSAFE.putAddress(oldTail,newAvailablePage); - } - UNSAFE.putAddress(newAvailablePage+ZMPAGE_PREVPAGE_OFFSET,oldTail); - UNSAFE.putAddress(newAvailablePage+ZMPAGE_NEXTPAGE_OFFSET, - addrAvailablePageTail); - UNSAFE.putAddress(addrAvailablePageTail,newAvailablePage); - - UNSAFE.putLong(addrNumAvailablePage, numAvailablePage+1); - } - - //TODO: some state changes may be not necessary? - /** - * contract: - *

1. numAvailablePage > 0 - *

2. removedPage in AvailablePages[sizeClassIndex] - */ - private static final void tlp_AvailablePages_remove( - long addrAvailablePageHead, - long removedPage) { - long availablePageTail = addrAvailablePageHead + SIZE_LONG_TYPE; - -// long head = UNSAFE.getAddress(); -// long tail = UNSAFE.getAddress(); - long addrPrevPage = removedPage+ZMPAGE_PREVPAGE_OFFSET; - long addrNextPage = removedPage+ZMPAGE_NEXTPAGE_OFFSET; - long prev = UNSAFE.getAddress(addrPrevPage); - long next = UNSAFE.getAddress(addrNextPage); - - if (prev!=addrAvailablePageHead) { - UNSAFE.putAddress(prev+ZMPAGE_NEXTPAGE_OFFSET,next); - } else {//prev is head - UNSAFE.putAddress(prev,next); - } - if (next!=availablePageTail) { - UNSAFE.putAddress(next+ZMPAGE_PREVPAGE_OFFSET,prev); - } else {//next is tail - UNSAFE.putAddress(next,prev); - } - - long addrNumAvailablePage = availablePageTail+SIZE_LONG_TYPE; - - UNSAFE.putLong(addrNumAvailablePage, - UNSAFE.getLong(addrNumAvailablePage)-1); - } - - //============================ - private static final void tlp_FreePages_addToHead(long addrFreePagesHead, - long newFreePage, - long addrNumFreePages) { - long numFreePages = UNSAFE.getLong(addrNumFreePages); - - long oldHead = UNSAFE.getAddress(addrFreePagesHead); - - if (numFreePages != 0) {//oldHead!=availablePageTail - UNSAFE.putAddress(oldHead + ZMPAGE_PREVFREEPAGE_OFFSET, newFreePage); - }else {//oldHead is tail - UNSAFE.putAddress(oldHead, newFreePage); - } - UNSAFE.putAddress(newFreePage + ZMPAGE_NEXTFREEPAGE_OFFSET, oldHead); - UNSAFE.putAddress(newFreePage + ZMPAGE_PREVFREEPAGE_OFFSET, - addrFreePagesHead); - UNSAFE.putAddress(addrFreePagesHead, newFreePage); - - UNSAFE.putLong(addrNumFreePages, numFreePages + 1); - } - - private static final void tlp_FreePages_remove(long addrFreePagesHead, - long addrFreePagesTail, - long addrNumFreePages, - long removedFreePage) { - long addrPrevFreePage = removedFreePage+ZMPAGE_PREVFREEPAGE_OFFSET; - long addrNextFreePage = removedFreePage+ZMPAGE_NEXTFREEPAGE_OFFSET; - long prev = UNSAFE.getAddress(addrPrevFreePage); - long next = UNSAFE.getAddress(addrNextFreePage); - - if (prev!=addrFreePagesHead) { - UNSAFE.putAddress(prev+ZMPAGE_NEXTFREEPAGE_OFFSET,next); - } else {//prev is head - UNSAFE.putAddress(prev,next); - } - if (next!=addrFreePagesTail) { - UNSAFE.putAddress(next+ZMPAGE_PREVFREEPAGE_OFFSET,prev); - } else {//next is tail - UNSAFE.putAddress(next,prev); - } - - UNSAFE.putLong(addrNumFreePages, UNSAFE.getLong(addrNumFreePages)-1); - } - - //============================================================================ - private static final void tlp_RemoteFreedChunksHead_add( - long addrRemoteFreedChunksTail, - long addrRemoteFreedChunk) { - UNSAFE.putAddress(addrRemoteFreedChunk,NULL_ADDRESS); -// UNSAFE.fullFence(); - long oldTail = UNSAFE.getAndSetLong(null, - addrRemoteFreedChunksTail, addrRemoteFreedChunk); - UNSAFE.putAddress(oldTail, addrRemoteFreedChunk); - UNSAFE.fullFence(); - } - - /* - * TODO: here we have no ABA problem: because we have a single consumer here, - * it is not possible to change the head without this method itself. - */ - private static final long tlp_RemoteFreedChunksHead_remove( - long addrRemoteFreedChunksHead, - long addrRemoteFreedChunksTail, - long addrRemoteFreedChunksDummy) { - UNSAFE.loadFence(); - long head = UNSAFE.getAddress(addrRemoteFreedChunksHead); - long v = UNSAFE.getAddress(head); - if (v!=NULL_ADDRESS) { - long next = UNSAFE.getAddress(v); - if (next==NULL_ADDRESS) {//v==tail(==getAddr(addrRemoteFreedChunksTail)) - if ( UNSAFE.compareAndSwapLong(null, - addrRemoteFreedChunksTail, - v, - addrRemoteFreedChunksDummy) ) { - return v; - } else { - return NULL_ADDRESS; - } - } else { - UNSAFE.putAddress(head, next); - } - UNSAFE.fullFence();// - return v; - } else { - return NULL_ADDRESS; - } - } - //============================================================================ - //Stats API - /** - * ManagedPoolStats provides statistics for the managed off-heap pool - * - */ - public static class ManagedPoolStats { - - - public static long totalManagedPoolSize() { - return sizeGP; - } - - public static long currentNumOfGPAvaiablePages() { - return UNSAFE.getLongVolatile(null, addressGPHead_NumAvailablePages); - } - - public static long currentNumOfTLPAvaiablePages(int sizeClassIndex) { - return currentNumOfTLPAvaiablePages(currentThreadId(), sizeClassIndex); - } - - public static long currentNumOfTLPAvaiablePages(long tid, - int sci) { - long addressTLP = addressTLPs + TLP_ITEM_SIZE *tid; - long addrAvailablePages = addressTLP + TLP_AVAILABLEPAGES_OFFSET; - long addrAvailablePageHead = addrAvailablePages + - sci* TLP_AVAILABLEPAGES_ITEM_SIZE; - long addrNumAvailablePage = addrAvailablePageHead+2*SIZE_LONG_TYPE; - return UNSAFE.getLongVolatile(null, addrNumAvailablePage); - } - - public static long currentNumOfTLPFreePages() { - return currentNumOfTLPFreePages(currentThreadId()); - } - - public static long currentNumOfTLPFreePages(long tid) { - long numFreePages = addressTLPs + TLP_ITEM_SIZE *tid + TLP_NUMFREEPAGES_OFFSET; - return UNSAFE.getLong(numFreePages); - } - - public static int currentNumOfPageAvailableChunks(long addressPage) { - return UNSAFE.getInt(addressPage+ZMPAGE_NUMAVAILABLECHUNKS_OFFSET); - } - - public static long currentNumOfTLPs() { - return UNSAFE.getIntVolatile(null, addressTLPHEAD);//XXX: TLPHEAD_NUMTLPS_OFFSET=0 - } - - public static final long queryAllocationSize(int sizeOfBytes) { - int sci = sizeClassIndex((int) sizeOfBytes); - return UNSAFE.getInt(addressSizeClassInfo+sci*SIZE_INT_TYPE); - } - - } - -} diff --git a/libtest/src/main/java/z/util/Contracts.java b/libtest/src/main/java/z/util/Contracts.java deleted file mode 100644 index 7710c34dd..000000000 --- a/libtest/src/main/java/z/util/Contracts.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import z.exception.ContractViolatedException; - -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; - -/** - * Contracts is static collection for facitiy - * - * @author Landz - */ -public class Contracts { - - private Contracts(){}; - - /** - * this constant - TRICK, is a trick for disabling all Contracts side - * in the compile-time for zero overhead. - * - * !note: This kind of flag is not excited for - * some hotspot optimization bug. - */ - private static final boolean TRICK = true; - - public static void contract(BooleanSupplier contractSupplier) { - if (TRICK && !contractSupplier.getAsBoolean()) - throw new ContractViolatedException("[Contract Breached]: " + - "the contract "+ contractSupplier + " fails to be kept!"); - } - - /** - * - * Every contract has a tag, which support the hierarchical form, - * like Java package naming. - *

- * The convention is to use the className.methodName for the tag, like: - *

{@code
-     *     contract()
-     * }
- * - * @param prefixedLabel - * @param contractSupplier - */ - public static final void contract(String prefixedLabel, - BooleanSupplier contractSupplier) { - if (TRICK && !contractSupplier.getAsBoolean()) - throw new ContractViolatedException(prefixedLabel + ":" + - " the contract "+ contractSupplier + - " fails to be kept!"); - } - - /** - * here, you can customize the type and content of your exception. - * - * @param contractSupplier - * @param exceptionSupplier - */ - public static final void contract(BooleanSupplier contractSupplier, - Supplier exceptionSupplier) { - if (TRICK && !contractSupplier.getAsBoolean()) - throw exceptionSupplier.get(); - } - - - - - -} diff --git a/libtest/src/main/java/z/util/MethodHandles.java b/libtest/src/main/java/z/util/MethodHandles.java deleted file mode 100644 index 3411ba221..000000000 --- a/libtest/src/main/java/z/util/MethodHandles.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.lang.reflect.Field; - -import static java.lang.invoke.MethodHandles.Lookup; -import static z.util.Unsafes.UNSAFE; - -/** - * All APIs to expose MethodHandles side things in Hotspot for use. - * - * @Performance, we keep all exposed publics in constant as possible - * - */ -public class MethodHandles { - - public static final Lookup LOOKUP; - static { - //XXX: hack to be GOD?... - Field f_IMPL_LOOKUP = Throwables.uncheckTo(() -> Lookup.class.getDeclaredField("IMPL_LOOKUP")); - Object base = UNSAFE.staticFieldBase(Lookup.class); - long offset = UNSAFE.staticFieldOffset(f_IMPL_LOOKUP); - LOOKUP = (Lookup)UNSAFE.getObject(base, offset); - } - -} diff --git a/libtest/src/main/java/z/util/SystemProperty.java b/libtest/src/main/java/z/util/SystemProperty.java deleted file mode 100644 index 06bbd5c06..000000000 --- a/libtest/src/main/java/z/util/SystemProperty.java +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * Represents a {@linkplain System#getProperties() standard system property}. - * - * @author Kurt Alfred Kluever - * @modified Landz - */ -public enum SystemProperty { - - /** Java Runtime Environment version. */ - JAVA_VERSION("java.version"), - - /** Java Runtime Environment vendor. */ - JAVA_VENDOR("java.vendor"), - - /** Java vendor URL. */ - JAVA_VENDOR_URL("java.vendor.url"), - - /** Java installation directory. */ - JAVA_HOME("java.home"), - - /** Java Virtual Machine specification version. */ - JAVA_VM_SPECIFICATION_VERSION("java.vm.specification.version"), - - /** Java Virtual Machine specification vendor. */ - JAVA_VM_SPECIFICATION_VENDOR("java.vm.specification.vendor"), - - /** Java Virtual Machine specification name. */ - JAVA_VM_SPECIFICATION_NAME("java.vm.specification.name"), - - /** Java Virtual Machine implementation version. */ - JAVA_VM_VERSION("java.vm.version"), - - /** Java Virtual Machine implementation vendor. */ - JAVA_VM_VENDOR("java.vm.vendor"), - - /** Java Virtual Machine implementation name. */ - JAVA_VM_NAME("java.vm.name"), - - /** Java Runtime Environment specification version. */ - JAVA_SPECIFICATION_VERSION("java.specification.version"), - - /** Java Runtime Environment specification vendor. */ - JAVA_SPECIFICATION_VENDOR("java.specification.vendor"), - - /** Java Runtime Environment specification name. */ - JAVA_SPECIFICATION_NAME("java.specification.name"), - - /** Java class format version number. */ - JAVA_CLASS_VERSION("java.class.version"), - - /** Java class path. */ - JAVA_CLASS_PATH("java.class.path"), - - /** List of paths to search when loading libraries. */ - JAVA_LIBRARY_PATH("java.library.path"), - - /** Default temp file path. */ - JAVA_IO_TMPDIR("java.io.tmpdir"), - - /** Name of JIT compiler to use. */ - JAVA_COMPILER("java.compiler"), - - /** Path of extension directory or directories. */ - JAVA_EXT_DIRS("java.ext.dirs"), - - /** Operating system name. */ - OS_NAME("os.name"), - - /** Operating system architecture. */ - OS_ARCH("os.arch"), - - /** Operating system version. */ - OS_VERSION("os.version"), - - /** File separator ("/" on UNIX). */ - FILE_SEPARATOR("file.separator"), - - /** Path separator (":" on UNIX). */ - PATH_SEPARATOR("path.separator"), - - /** Line separator ("\n" on UNIX). */ - LINE_SEPARATOR("line.separator"), - - /** User's account name. */ - USER_NAME("user.name"), - - /** User's home directory. */ - USER_HOME("user.home"), - - /** User's current working directory. */ - USER_DIR("user.dir"), - - /** - * to specify the initial size of ZMalloc's global pool in mega bytes. - * see more, {@link z.offheap.zmalloc.Allocator#sizeGP} - */ - ZMALLOC_INITIAL_POOLSIZE("z.offheap.zmalloc.initialPoolSize"), - - /** - * to specify the threshold number of free pages, to which number the ZMalloc - * will start to return the unused pages(a.k.a., freePage) to global pool. - *

- * The default value for this property is - * {@link z.offheap.zmalloc.Allocator#FREEPAGES_NUM_THRESHOLD_DEFAULT} - *

- * see more, {@link z.offheap.zmalloc.Allocator#freePagesNumThreshold} - * and {@link #ZMALLOC_FREEPAGES_NUM_TORETURN} - */ - ZMALLOC_FREEPAGES_NUM_THRESHOLD("z.offheap.zmalloc.freepages.numThreshold"), - - /** - * to specify the return number of free pages to global pool when the - * {@link #ZMALLOC_FREEPAGES_NUM_THRESHOLD} reached. - *

- * The default value for this property is - * {@link z.offheap.zmalloc.Allocator#FREEPAGES_NUM_TORETURN_DEFAULT} - *

- * see more, {@link z.offheap.zmalloc.Allocator#freePagesNumToReturn} - * and {@link #ZMALLOC_FREEPAGES_NUM_THRESHOLD} - */ - ZMALLOC_FREEPAGES_NUM_TORETURN("z.offheap.zmalloc.freepages.toReturn"), - - /** not used now */ - ZMALLOC_MAX_POOLSIZE("z.offheap.zmalloc.maxPoolSize"); - - - - - private final String key; - - private SystemProperty(String key) { - this.key = key; - } - - /** - * Returns the key used to lookup this system property. - */ - public String key() { - return key; - } - - /** - * Returns the current value for this system property by delegating to - * {@link System#getProperty(String)}. - */ - public String value() { - return System.getProperty(key); - } - - /** - * Sets the system property indicated by the specified key. - *

- * this method now delegates to {@link System#setProperty(String, String)}. - *

- * - * @param value the value of the system property. - * @return the previous value of the system property, - * or null if it did not have one. - - */ - public String setValue(String value) { - return System.setProperty(key, value); - } - - /** - * Returns a string representation of this system property. - */ - @Override public String toString() { - return key() + "=" + value(); - } -} diff --git a/libtest/src/main/java/z/util/Throwables.java b/libtest/src/main/java/z/util/Throwables.java deleted file mode 100644 index 741f717b3..000000000 --- a/libtest/src/main/java/z/util/Throwables.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import z.annotation.PerformanceDegraded; -import z.function.ThrowableFunction0; -import z.function.ThrowableRunnable; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.function.Function; - -/** - * Throwables provides various verbs for exception side operations. - */ -public final class Throwables { - - private static final - Function - DEFAULT_EXCEPTION_SUPPLIER = RuntimeException::new; - - public static void uncheck(ThrowableRunnable runnable, - Function expThrower) { - try { - runnable.run(); - } catch (RuntimeException e) { - throw e; - } catch (Throwable e) { - throw expThrower.apply(e); - } - } - - public static void uncheck(ThrowableRunnable fn) { - uncheck(fn, DEFAULT_EXCEPTION_SUPPLIER); - } - - /** - * this static method will return the result of the lambda apply - * - * @param fn - * @param expThrower - * @param - * @return - */ - public static R uncheckTo(ThrowableFunction0 fn, - Function expThrower) { - try { - return fn.apply(); - } catch (Throwable e) { - throw expThrower.apply(e); - } - } - - /** - * this static method will return the result of the lambda apply - * - * @param fn - * @param - * @return - */ - public static R uncheckTo(ThrowableFunction0 fn) { - return uncheckTo(fn, DEFAULT_EXCEPTION_SUPPLIER); - } - - - /** - * note: this method marked with @PerformanceDegraded - * - * @param runnable - * @return - */ - @PerformanceDegraded("z.util.Throwables.ExceptionMatcher") - public static ExceptionMatcher check(ThrowableRunnable runnable) { - return (expClass, expHandler) -> { - try { - runnable.run(); - } catch (Throwable e) { - if (e.getClass().equals(expClass)) - expHandler.handle(e); - throw new RuntimeException(e); - } - }; - - } - - @FunctionalInterface - public static interface ExceptionHandler { - void handle(Throwable e); - } - - @FunctionalInterface - public static interface ExceptionMatcher { - - default void onExceptions(Class[] expClass, ExceptionHandler expHandler) { - } - - void onException(Class expClass, ExceptionHandler expHandler); - } - -// //========================================= ... check ... onException ... -// @PerformanceDegraded -// public static ExceptionMatcher onException(Class expClass, ExceptionHandler expHandler) { -// return (ec, eh) -> { -// Stream.builder().add(); -// }; -// } -// -// @FunctionalInterface -// public static interface SelfReturnableExceptionCollector { -// -// SelfReturnableExceptionCollector onException(Class expClass, ExceptionHandler expHandler); -// -// } - - /** - * Returns a string containing the result of - * {@link Throwable#toString() toString()}, followed by the full, recursive - * stack trace of {@code throwable}. Note that you probably should not be - * parsing the resulting string; if you need programmatic access to the stack - * frames, you can call {@link Throwable#getStackTrace()}. - * - * @origin guava - */ - public static String getStackTraceAsString(Throwable throwable) { - StringWriter stringWriter = new StringWriter(); - throwable.printStackTrace(new PrintWriter(stringWriter)); - return stringWriter.toString(); - } - -} \ No newline at end of file diff --git a/libtest/src/main/java/z/util/Unsafes.java b/libtest/src/main/java/z/util/Unsafes.java deleted file mode 100644 index 6a929e460..000000000 --- a/libtest/src/main/java/z/util/Unsafes.java +++ /dev/null @@ -1,633 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import sun.misc.Unsafe; -import z.annotation.NotThreadSafe; - -import java.lang.reflect.Field; - -import static z.util.Throwables.uncheckTo; - -/** - * All APIs to expose Unsafe side things in Hotspot for use. - * - * @Performance, we keep all exposed publics in constant as possible - */ -public class Unsafes { - - public static final int SIZE_BYTE_TYPE = Byte.BYTES; - public static final int SIZE_INT_TYPE = Integer.BYTES; - public static final int SIZE_SHORT_TYPE = Short.BYTES; - public static final int SIZE_CHAR_TYPE = Character.BYTES; - public static final int SIZE_LONG_TYPE = Long.BYTES; - public static final int SIZE_FLOAT_TYPE = Float.BYTES; - public static final int SIZE_DOUBLE_TYPE = Double.BYTES; - - - public static final Unsafe UNSAFE = uncheckTo(() -> { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(null); - }); - - static final boolean is64bit = true; // auto detect if possible.public static void printAddresses(java.lang.String label, java.lang.Object... objects) { - - public static void printAddresses(String label, Object... objects) { - System.out.print(label + ": 0x"); - long last = 0; - int offset = UNSAFE.arrayBaseOffset(objects.getClass()); - int scale = UNSAFE.arrayIndexScale(objects.getClass()); - switch (scale) { - case 4: - long factor = is64bit ? 8 : 1; - final long i1 = (UNSAFE.getInt(objects, offset) & 0xFFFFFFFFL) * factor; - System.out.print(Long.toHexString(i1)); - last = i1; - for (int i = 1; i < objects.length; i++) { - final long i2 = (UNSAFE.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor; - if (i2 > last) - System.out.print(", +" + Long.toHexString(i2 - last)); - else - System.out.print(", -" + Long.toHexString(last - i2)); - last = i2; - } - break; - case 8: - throw new AssertionError("Not supported"); - } - - System.out.println(); - } - - /**the size (in bytes) of a native memory page. */ - public static final int SIZE_PAGE = UNSAFE.pageSize(); - - public static final boolean isPageAligned(long address) { - return (address & (SIZE_PAGE-1))==0; - } - - /** - * return the next page aligned address no matter the current address is - * aligned or not.

- * @param address - * @return - */ - public static final long nextPageAlignedAddress(long address) { - return address-(address&(SIZE_PAGE-1))+SIZE_PAGE; - } - - /** - * return the next and nearest page aligned address. If the address has - * already aligned, return input address itself. - *

- * see aslo {@link #nextPageAlignedAddress} - * @param address - * @return - */ - public static final long nextNearestPageAlignedAddress(long address) { - long masked = address&(SIZE_PAGE-1); - if (masked!=0) { - return address-masked+SIZE_PAGE; - } else { - return address; - } - } - - /** NOTE: SIZE_CACHE_LINE is CPU dependent, now for x86-64/x86. */ - public static final int SIZE_CACHE_LINE = 64; - /** NOTE: Doubling for modern adjacent cache-line prefetch. */ - public static final int SIZE_CACHE_LINE_PADDING = 2*SIZE_CACHE_LINE; - private static final int cacheLineSize() { - return 64; - } - - public static final boolean isCacheLineAligned(long address) { - return (address&(SIZE_CACHE_LINE-1))==0; - } - - public static final long nextCacheLineAlignedAddress(long address) { - return address-(address&(SIZE_CACHE_LINE-1))+SIZE_CACHE_LINE; - } - - public static final long nextNearestCacheLineAlignedAddress(long address) { - long masked = address&(SIZE_CACHE_LINE-1); - if (masked!=0) { - return address-masked+SIZE_CACHE_LINE; - } else { - return address; - } - } - - /** The value of addressSize */ - public static final int SIZE_ADDRESS = UNSAFE.ADDRESS_SIZE; - - public static final long nextMachineWordAlignedAddress(long address) { - return address-(address&(SIZE_ADDRESS-1))+SIZE_ADDRESS; - } - - public static final long nextNearestMachineWordAlignedAddress(long address) { - long masked = address&(SIZE_ADDRESS-1); - if (masked!=0) { - return address-masked+SIZE_ADDRESS; - } else { - return address; - } - } - - static long address(Object obj) { - Object[] array = new Object[]{obj}; - long baseOffset = UNSAFE.arrayBaseOffset(Object[].class); - return UNSAFE.getInt(array, baseOffset); - } - - public static boolean isArchIntel64() { - String arch = SystemProperty.OS_ARCH.value(); - return arch.equals("amd64") || arch.equals("x86_64"); - - } - - public static boolean isArchIntel32() { - String arch = SystemProperty.OS_ARCH.value(); - return arch.equals("i386") || arch.equals("x86"); - - } - - /** - * Note: here x86 arch includes x86 and x86-64 - * @return - */ - public static boolean isArchX86() { - String arch = SystemProperty.OS_ARCH.value(); - return arch.equals("i386") || arch.equals("x86") - || arch.equals("amd64") || arch.equals("x86_64"); - } - - /** - * a helper on {@link Unsafe#setMemory} to zero the offheap buffer. - * @param addressOfBuffer - * @param sizeOfBytes - */ - public static void systemClearMemory(long addressOfBuffer, int sizeOfBytes) { - switch (sizeOfBytes) { - case 1 : - UNSAFE.putByte(addressOfBuffer,(byte)0); - break; - case 2: - UNSAFE.putShort(addressOfBuffer, (short) 0); - break; - case 4: - UNSAFE.putInt(addressOfBuffer, 0); - break; - case 8: - UNSAFE.putLong(addressOfBuffer, 0); - break; - case 16: - UNSAFE.putLong(addressOfBuffer, 0); - UNSAFE.putLong(addressOfBuffer+8,0); - break; - case 32: - UNSAFE.putLong(addressOfBuffer,0); - UNSAFE.putLong(addressOfBuffer+8,0); - UNSAFE.putLong(addressOfBuffer+16,0); - UNSAFE.putLong(addressOfBuffer+24,0); - break; - default: - UNSAFE.setMemory(addressOfBuffer, sizeOfBytes, (byte)0); - } - } - - - /** - * Allocates a new block of native memory, of the given size in byte unit. - * The contents of the memory are uninitialized; they will generally be - * garbage. The resulting native pointer will never be zero, and will be - * aligned for all value types. - * - * @throws IllegalArgumentException if the size is negative or too large - * for the native size_t type - * - * @throws OutOfMemoryError if the allocation is refused by the system - */ - public static long systemAllocateMemory(long sizeOfBytes) { - return UNSAFE.allocateMemory(sizeOfBytes); - } - - /** - * Disposes of a block of native memory, as obtained from {@link - * #systemAllocateMemory}. The currentAddress passed to this method may be null, in which - * case no action is taken. - * - * @see #systemAllocateMemory - */ - public static void systemFreeMemory(long address) { - UNSAFE.freeMemory(address); - } - - //====================================================================== - // DSL for currentAddress - - /** - * NOTE:

- * for support friendly DSL style, we still need OnAddress instance... - * - * XXX: is @NotThreadSafe OK? - */ - @NotThreadSafe - private static class OnAddressImpl implements OnAddress, OnAddressFollowBy { - private OnAddressImpl() {} - - private long startAddress = 0L; - private long currentAddress = 0L; - - private static final OnAddress startOn(long address) { - OnAddressImpl instance = new OnAddressImpl(); - instance.startAddress = address; - instance.currentAddress = address; - return instance; - } - - @Override - public final OnAddressFollowBy put(int value){ - UNSAFE.putInt(currentAddress,value); - currentAddress += 4; - return this; - } - - @Override - public final OnAddressFollowBy put(long value){ - UNSAFE.putLong(currentAddress, value); - currentAddress += 8; - return this; - } - - @Override - public final OnAddressFollowBy put(byte value){ - UNSAFE.putByte(currentAddress,value); - currentAddress += 1; - return this; - } - - @Override - public final OnAddressFollowBy put(short value){ - UNSAFE.putShort(currentAddress, value); - currentAddress += 2; - return this; - } - - @Override - public final OnAddressFollowBy put(float value){ - UNSAFE.putFloat(currentAddress, value); - currentAddress += 4; - return this; - } - - @Override - public final OnAddressFollowBy put(double value){ - UNSAFE.putDouble(currentAddress, value); - currentAddress += 8; - return this; - } - - @Override - public final OnAddressFollowBy putAddress(long address){ - UNSAFE.putAddress(currentAddress,address); - currentAddress += SIZE_ADDRESS; - return this; - } - - @Override - public OnAddressFollowBy self() { - return this; - } - - @Override - public final OnAddressFollowBy followBy(int value){ - put(value); - return this; - } - - @Override - public OnAddressFollowBy followBy(long value) { - put(value); - return this; - } - - @Override - public OnAddressFollowBy followBy(byte value) { - put(value); - return this; - } - - @Override - public OnAddressFollowBy followBy(short value) { - put(value); - return this; - } - - @Override - public OnAddressFollowBy followBy(float value) { - put(value); - return this; - } - - @Override - public OnAddressFollowBy followBy(double value) { - put(value); - return this; - } - - @Override - public OnAddressFollowBy followByAddress(long address) { - putAddress(address); - return this; - } - - @Override - public OnAddressFollowBy paddedBy(long length, byte paddingValue) { - UNSAFE.setMemory(currentAddress, length, paddingValue); - currentAddress += length; - return this; - } - - @Override - public OnAddressFollowBy paddedBy(long length) { - paddedBy(length, (byte) 0); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextNearestMachineWordAlignedAddress() { - long addr = nextNearestMachineWordAlignedAddress(currentAddress); - long length = addr-currentAddress; - if (length!=0) - paddedBy(length); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextNearestCacheLineAlignedAddress() { - long addr = nextNearestCacheLineAlignedAddress(currentAddress); - long length = addr-currentAddress; - if (length!=0) - paddedBy(length); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextNearestPageAlignedAddress() { - long addr = nextNearestPageAlignedAddress(currentAddress); - long length = addr-currentAddress; - if (length!=0) - paddedBy(length); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextMachineWordAlignedAddress() { - paddedBy(nextMachineWordAlignedAddress(currentAddress) -currentAddress); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextCacheLineAlignedAddress() { - paddedBy(nextCacheLineAlignedAddress(currentAddress) -currentAddress); - return this; - } - - @Override - public OnAddressFollowBy paddedToNextPageAlignedAddress() { - paddedBy(nextPageAlignedAddress(currentAddress) -currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextNearestMachineWordAlignedAddress() { - currentAddress = nextNearestMachineWordAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextNearestCacheLineAlignedAddress() { - currentAddress = nextNearestCacheLineAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextNearestPageAlignedAddress() { - currentAddress = nextNearestPageAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextMachineWordAlignedAddress() { - currentAddress = nextMachineWordAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextCacheLineAlignedAddress() { - currentAddress = nextCacheLineAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoNextPageAlignedAddress() { - currentAddress = nextPageAlignedAddress(currentAddress); - return this; - } - - @Override - public OnAddressFollowBy gotoAddress(long address) { - currentAddress = address; - return this; - } - - @Override - public long endAddress() { - return currentAddress; - } - - @Override - public byte[] toByteArray() { - //length>=1 - int length = (int)(currentAddress-startAddress); - byte[] rt = new byte[length]; - long offset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; - UNSAFE.copyMemory(null,startAddress,rt,offset,length); - return rt; - } - - @Override - public void toByteArray(byte[] dst) { - //contract(()->dst.length>0); - int lengthSrc = (int)(currentAddress-startAddress+1); - int lengthDst = dst.length; - int minLength = lengthSrc - * NOTE:

- * 1. currentAddress is unsigned int

- * 2. currentAddress size depends on the system

- * 3. use currentAddress as currentAddress for all usages

- * (Don't mess up with long unless you know what you are doing)

- */ - public OnAddressFollowBy putAddress(long address); - - /** - * return the instance itself as a convenient method to - * start padding directly. - */ - public OnAddressFollowBy self(); - } - - - public static interface OnAddressFollowBy { - public OnAddressFollowBy followBy(int value); - public OnAddressFollowBy followBy(long value); - public OnAddressFollowBy followBy(byte value); - public OnAddressFollowBy followBy(short value); - public OnAddressFollowBy followBy(float value); - public OnAddressFollowBy followBy(double value); - public OnAddressFollowBy followByAddress(long address); - - /** - * the current address will be padded wtih some paddingValue in some length. - *

- */ - public OnAddressFollowBy paddedBy(long length, byte paddingValue); - - /** - * same to call with paddedBy(length, 0) - *

- */ - public OnAddressFollowBy paddedBy(long length); - - public OnAddressFollowBy paddedToNextNearestMachineWordAlignedAddress(); - public OnAddressFollowBy paddedToNextNearestCacheLineAlignedAddress(); - public OnAddressFollowBy paddedToNextNearestPageAlignedAddress(); - - public OnAddressFollowBy paddedToNextMachineWordAlignedAddress(); - public OnAddressFollowBy paddedToNextCacheLineAlignedAddress(); - public OnAddressFollowBy paddedToNextPageAlignedAddress(); - - public OnAddressFollowBy gotoNextNearestMachineWordAlignedAddress(); - public OnAddressFollowBy gotoNextNearestCacheLineAlignedAddress(); - public OnAddressFollowBy gotoNextNearestPageAlignedAddress(); - - public OnAddressFollowBy gotoNextMachineWordAlignedAddress(); - public OnAddressFollowBy gotoNextCacheLineAlignedAddress(); - public OnAddressFollowBy gotoNextPageAlignedAddress(); - - public OnAddressFollowBy gotoAddress(long address); - - public long endAddress(); - - /** - * XXX: we may need an end() to seal the whole DSL? - *

- * @return dst - a new byte array contains the content from starting to - * current address. - */ - public byte[] toByteArray(); - - /** - * variant of {@link #toByteArray()} - * contract: dst.length>0 - *

- * the content of dst will be dst.length bytes of the content from starting - * to current address. The remaining content of dst after outputting is - * untouchded. - * - */ - public void toByteArray(byte[] dst); - } - - - public static final OnAddress onAddress(long address) { - return OnAddressImpl.startOn(address); - } - - - /** - * Returns the thread id for the given thread. We must access - * this directly rather than via method Thread.getId() because - * getId() is not final, and has been known to be overridden in - * ways that do not preserve unique mappings. - */ - private static final long TID_OFFSET = uncheckTo(() -> { - return UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("tid")); - }); - - /** - * we don't use volatile version, based on the following reasons/assumptions: - * 1. init() called by Thread constructor is before start(); - * 2. "A call to start() on a thread happens-before any actions in the - * started thread"; - * 3. there is no tid modifier (in source code form) except Thread itself - * (so "effective final"); - * - * @param thread - * @return - */ - public static final long threadId(Thread thread) { - return UNSAFE.getLong(thread, TID_OFFSET); - } - - public static final long currentThreadId() { - return UNSAFE.getLong(Thread.currentThread(), TID_OFFSET); - } - - -} \ No newline at end of file diff --git a/libtest/src/main/java/z/util/concurrent/ThreadLocalPool.java b/libtest/src/main/java/z/util/concurrent/ThreadLocalPool.java deleted file mode 100644 index 228064f5f..000000000 --- a/libtest/src/main/java/z/util/concurrent/ThreadLocalPool.java +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import z.util.primitives.Ints; - -import java.util.function.Supplier; - -import static z.util.Contracts.contract; -import static z.util.Unsafes.currentThreadId; - - -/** -* -* ThreadLocalPool, is a pool for type T. This may be good for "heavy" object. -* But, ThreadLocalPool, as a "heavy" object container, itself is "heavy". And, -* this ThreadLocalPool does not support "recycle". Once, you need to recycle -* pooled objects, you may need to recycle often. Then, the sense of pool -* has been gone. -*

-* The better way is to estimate the maximum cursor of your ThreadLocalPool. -* However, you would get null when no object is available at calling point. -* You should check against this if needed. -*

-* Ideas: -* 1. too large pool is still not friendly to GC; -* -*

-* Note: -* this ThreadLocalPool instance only support the long-living Thread -* usage now, although this case may be improved in the future when some low level -* hooking ready. -* -*/ - -public class ThreadLocalPool implements AutoCloseable { - - private static final int MAX_SUPPORTED_THREAD_ID = 1024;//FIXME - //TODO: false sharing - private final ItemStack[] tlStacks = new ItemStack[MAX_SUPPORTED_THREAD_ID]; - - private final int incrementStep; - private final int capacity; - private final Supplier supplier;//return supplier.get(); - - public ThreadLocalPool(int capacity, Supplier supplier) { - this(capacity, capacity, supplier); - } - - public ThreadLocalPool(int capacity, int incrementStep, Supplier supplier) { - contract( - () -> Ints.isPowerOfTwo(capacity) - && Ints.isPowerOfTwo(incrementStep) - && (incrementStep<=capacity) , - () -> new IllegalArgumentException("Now the capacity " + - "is supported to be the power of 2 only."));//FIXME - - this.capacity = capacity; - this.incrementStep = incrementStep; - this.supplier = supplier; - } - - - /** - * return one pooled item which contains type T object, or return null - * immediately if no item is available. - */ - public Item item() { - int tid = (int)currentThreadId(); - //FIXME as contract - if (tid==MAX_SUPPORTED_THREAD_ID) - throw new IllegalStateException( - "the supported threadId should be less than " - +MAX_SUPPORTED_THREAD_ID); - - ItemStack stack = tlStacks[tid]; - if (stack==null) { - stack = new ItemStack(incrementStep, incrementStep!=capacity, supplier); - tlStacks[tid] = stack; - } - - if (stack.cursor ==0 && stack.notMaxCapacity) { - if (capacity<=(stack.capacity+incrementStep)) { - stack.enlarge(incrementStep); - if (capacity==stack.capacity) - stack.notMaxCapacity = false; - } - } - - return stack.pop(); - } - - @Override - /** - * release the pool's all resources - */ - public void close() throws Exception { - for (int i = 0; i < tlStacks.length; i++) { - ItemStack stack = tlStacks[i]; - if (stack!=null) { - Item[] items = stack.items; - for (int j = 0; j < items.length; j++) { - Object obj = items[j].get(); - if (obj instanceof AutoCloseable) - ((AutoCloseable)obj).close(); - items[j] = null; - } - tlStacks[i] = null; - } - } - } - - public static class Item implements AutoCloseable { - T object; - final ItemStack stack; - - Item(T object, ItemStack stack) { - this.object = object; - this.stack = stack; - } - - public T get() { - return object; - } - - @Override - public void close() { - stack.push(this); - } - } - - - private static class ItemStack { - - private final Supplier supplier; - - private Item[] items; - private int capacity; - private int cursor; - - boolean notMaxCapacity; - - ItemStack(int initSize, boolean notMaxCapacity, Supplier supplier) { - this.supplier = supplier; - this.notMaxCapacity = notMaxCapacity; - enlarge(initSize); - } - - Item pop() { - if (cursor == 0) { - return null; - } - return items[--cursor]; - } - - void push(Item item) { - items[cursor++] = item; - } - - void enlarge(int incrementStep) { - int newCapacity = capacity+incrementStep; - Item[] newItems = new Item[newCapacity]; - if (items!=null) { - System.arraycopy(items, 0, newItems, capacity, incrementStep); - } - items = newItems; - capacity = newCapacity; - cursor += incrementStep; - //initialize the empty slot - for (int i = 0; i < incrementStep; i++) { - items[i]=new Item(supplier.get(), this); - } - } - - } - -} - diff --git a/libtest/src/main/java/z/util/concurrent/ThreadLocalValueHolder.java b/libtest/src/main/java/z/util/concurrent/ThreadLocalValueHolder.java deleted file mode 100644 index 7791e7743..000000000 --- a/libtest/src/main/java/z/util/concurrent/ThreadLocalValueHolder.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.concurrent; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.function.Supplier; - -import static z.util.Unsafes.currentThreadId; - -/** - */ -public class ThreadLocalValueHolder implements AutoCloseable { - - private static final int MAX_SUPPORTED_THREAD_ID = 1024;//FIXME - - //TODO: false sharing - private final T[] tlValues = (T[])new Object[MAX_SUPPORTED_THREAD_ID]; - private final Supplier supplier;//return supplier.get(); - - public ThreadLocalValueHolder(Supplier supplier) { - this.supplier = supplier; - } - - public T get() { - int tid = (int)currentThreadId(); - //FIXME as contract - if (tid==MAX_SUPPORTED_THREAD_ID) - throw new IllegalStateException( - "the supported threadId should be less than " - +MAX_SUPPORTED_THREAD_ID); - - T rt = tlValues[tid]; - if (rt==null) { - rt = supplier.get(); - tlValues[tid] = rt; - } - return rt; - } - - @Override - public void close() throws Exception { - for (int i = 0; i < tlValues.length; i++) { - T tlValue = tlValues[i]; - if (tlValue instanceof AutoCloseable) - ((AutoCloseable)tlValue).close(); - tlValues[i] = null; - } - } -} diff --git a/libtest/src/main/java/z/util/primitives/Bytes.java b/libtest/src/main/java/z/util/primitives/Bytes.java deleted file mode 100644 index 6551e8b9e..000000000 --- a/libtest/src/main/java/z/util/primitives/Bytes.java +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.Serializable; -import java.util.*; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code byte} primitives, that are not - * already found in either {@link Byte} or {@link java.util.Arrays}, and interpret - * bytes as neither signed nor unsigned. The methods which specifically - * treat bytes as signed or unsigned are found in {@link SignedBytes} and {@link - * UnsignedBytes}. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - * @since 1.0 - */ -// TODO(kevinb): how to prevent warning on UnsignedBytes when building GWT -// javadoc? -public final class Bytes { - private Bytes() {} - - /** - * Returns {@code true} if {@code target} is present as an element anywhere in - * {@code array}. - * - * @param array an array of {@code byte} values, possibly empty - * @param target a primitive {@code byte} value - * @return {@code true} if {@code array[i] == target} for some value of {@code - * i} - */ - public static boolean contains(byte[] array, byte target) { - for (byte value : array) { - if (value == target) { - return true; - } - } - return false; - } - - /** - * Returns the index of the first appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code byte} values, possibly empty - * @param target a primitive {@code byte} value - * @return the least index {@code i} for which {@code array[i] == target}, or - * {@code -1} if no such index exists. - */ - public static int indexOf(byte[] array, byte target) { - return indexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int indexOf( - byte[] array, byte target, int start, int end) { - for (int i = start; i < end; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the start position of the first occurrence of the specified {@code - * target} within {@code array}, or {@code -1} if there is no such occurrence. - * - *

More formally, returns the lowest index {@code i} such that {@code - * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly - * the same elements as {@code target}. - * - * @param array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(byte[] array, byte[] target) { - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Returns the index of the last appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code byte} values, possibly empty - * @param target a primitive {@code byte} value - * @return the greatest index {@code i} for which {@code array[i] == target}, - * or {@code -1} if no such index exists. - */ - public static int lastIndexOf(byte[] array, byte target) { - return lastIndexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int lastIndexOf( - byte[] array, byte target, int start, int end) { - for (int i = end - 1; i >= start; i--) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new byte[] {a, b}, new byte[] {}, new - * byte[] {c}} returns the array {@code {a, b, c}}. - * - * @param arrays zero or more {@code byte} arrays - * @return a single array containing all the values from the source arrays, in - * order - */ - public static byte[] concat(byte[]... arrays) { - int length = 0; - for (byte[] array : arrays) { - length += array.length; - } - byte[] result = new byte[length]; - int pos = 0; - for (byte[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; - } - return result; - } - - /** - * Returns an array containing the same values as {@code array}, but - * guaranteed to be of a specified minimum length. If {@code array} already - * has a length of at least {@code minLength}, it is returned directly. - * Otherwise, a new array of size {@code minLength + padding} is returned, - * containing the values of {@code array}, and zeroes in the remaining places. - * - * @param array the source array - * @param minLength the minimum length the returned array must guarantee - * @param padding an extra amount to "grow" the array by if growth is - * necessary - * @throws IllegalArgumentException if {@code minLength} or {@code padding} is - * negative - * @return an array containing the values of {@code array}, with guaranteed - * minimum length {@code minLength} - */ - public static byte[] ensureCapacity( - byte[] array, int minLength, int padding) { - return (array.length < minLength) - ? copyOf(array, minLength + padding) - : array; - } - - // Arrays.copyOf() requires Java 6 - private static byte[] copyOf(byte[] original, int length) { - byte[] copy = new byte[length]; - System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); - return copy; - } - - /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code byte} value in the manner of {@link Number#byteValue}. - * - *

Elements are copied from the argument collection as if by {@code - * collection.toArray()}. Calling this method is as thread-safe as calling - * that method. - * - * @param collection a collection of {@code Number} instances - * @return an array containing the same values as {@code collection}, in the - * same order, converted to primitives - * @throws NullPointerException if {@code collection} or any of its elements - * is null - * @since 1.0 (parameter was {@code Collection} before 12.0) - */ - public static byte[] toArray(Collection collection) { - if (collection instanceof ByteArrayAsList) { - return ((ByteArrayAsList) collection).toByteArray(); - } - - Object[] boxedArray = collection.toArray(); - int len = boxedArray.length; - byte[] array = new byte[len]; - for (int i = 0; i < len; i++) { - // checkNotNull for GWT (do not optimize) - array[i] = ((Number)boxedArray[i]).byteValue(); - } - return array; - } - - /** - * Returns a fixed-size list backed by the specified array, similar to {@link - * java.util.Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, - * but any attempt to set a value to {@code null} will result in a {@link - * NullPointerException}. - * - *

The returned list maintains the values, but not the identities, of - * {@code Byte} objects written to or read from it. For example, whether - * {@code list.get(0) == list.get(0)} is true for the returned list is - * unspecified. - * - * @param backingArray the array to back the list - * @return a list view of the array - */ - public static List asList(byte... backingArray) { - if (backingArray.length == 0) { - return Collections.emptyList(); - } - return new ByteArrayAsList(backingArray); - } - - private static class ByteArrayAsList extends AbstractList - implements RandomAccess, Serializable { - final byte[] array; - final int start; - final int end; - - ByteArrayAsList(byte[] array) { - this(array, 0, array.length); - } - - ByteArrayAsList(byte[] array, int start, int end) { - this.array = array; - this.start = start; - this.end = end; - } - - @Override public int size() { - return end - start; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Byte get(int index) { - return array[start + index]; - } - - @Override public boolean contains(Object target) { - // Overridden to prevent a ton of boxing - return (target instanceof Byte) - && Bytes.indexOf(array, (Byte) target, start, end) != -1; - } - - @Override public int indexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Byte) { - int i = Bytes.indexOf(array, (Byte) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public int lastIndexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Byte) { - int i = Bytes.lastIndexOf(array, (Byte) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public Byte set(int index, Byte element) { - byte oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = element; - return oldValue; - } - - @Override public List subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex == toIndex) { - return Collections.emptyList(); - } - return new ByteArrayAsList(array, start + fromIndex, start + toIndex); - } - - @Override public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof ByteArrayAsList) { - ByteArrayAsList that = (ByteArrayAsList) object; - int size = size(); - if (that.size() != size) { - return false; - } - for (int i = 0; i < size; i++) { - if (array[start + i] != that.array[that.start + i]) { - return false; - } - } - return true; - } - return super.equals(object); - } - - @Override public int hashCode() { - int result = 1; - for (int i = start; i < end; i++) { - result = 31 * result + array[i]; - } - return result; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 5); - builder.append('[').append(array[start]); - for (int i = start + 1; i < end; i++) { - builder.append(", ").append(array[i]); - } - return builder.append(']').toString(); - } - - byte[] toByteArray() { - // Arrays.copyOfRange() is not available under GWT - int size = size(); - byte[] result = new byte[size]; - System.arraycopy(array, start, result, 0, size); - return result; - } - - private static final long serialVersionUID = 0; - } -} diff --git a/libtest/src/main/java/z/util/primitives/Chars.java b/libtest/src/main/java/z/util/primitives/Chars.java deleted file mode 100644 index ac2f3fbf1..000000000 --- a/libtest/src/main/java/z/util/primitives/Chars.java +++ /dev/null @@ -1,586 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.Serializable; -import java.util.*; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code char} primitives, that are not - * already found in either {@link Character} or {@link java.util.Arrays}. - * - *

All the operations in this class treat {@code char} values strictly - * numerically; they are neither Unicode-aware nor locale-dependent. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - * @since 1.0 - */ -public final class Chars { - private Chars() {} - - /** - * The number of bytes required to represent a primitive {@code char} - * value. - */ - public static final int BYTES = Character.SIZE / Byte.SIZE; - - /** - * Returns a hash code for {@code value}; equal to the result of invoking - * {@code ((Character) value).hashCode()}. - * - * @param value a primitive {@code char} value - * @return a hash code for the value - */ - public static int hashCode(char value) { - return value; - } - - /** - * Returns the {@code char} value that is equal to {@code value}, if possible. - * - * @param value any value in the range of the {@code char} type - * @return the {@code char} value that equals {@code value} - * @throws IllegalArgumentException if {@code value} is greater than {@link - * Character#MAX_VALUE} or less than {@link Character#MIN_VALUE} - */ - public static char checkedCast(long value) { - char result = (char) value; - if (result != value) { - // don't use checkArgument here, to avoid boxing - throw new IllegalArgumentException("Out of range: " + value); - } - return result; - } - - /** - * Returns the {@code char} nearest in value to {@code value}. - * - * @param value any {@code long} value - * @return the same value cast to {@code char} if it is in the range of the - * {@code char} type, {@link Character#MAX_VALUE} if it is too large, - * or {@link Character#MIN_VALUE} if it is too small - */ - public static char saturatedCast(long value) { - if (value > Character.MAX_VALUE) { - return Character.MAX_VALUE; - } - if (value < Character.MIN_VALUE) { - return Character.MIN_VALUE; - } - return (char) value; - } - - /** - * Compares the two specified {@code char} values. The sign of the value - * returned is the same as that of {@code ((Character) a).compareTo(b)}. - * - *

Note: projects using JDK 7 or later should use the equivalent - * {@link Character#compare} method instead. - * - * @param a the first {@code char} to compare - * @param b the second {@code char} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive - * value if {@code a} is greater than {@code b}; or zero if they are equal - */ - // TODO(kevinb): if JDK 6 ever becomes a non-concern, remove this - public static int compare(char a, char b) { - return a - b; // safe due to restricted range - } - - /** - * Returns {@code true} if {@code target} is present as an element anywhere in - * {@code array}. - * - * @param array an array of {@code char} values, possibly empty - * @param target a primitive {@code char} value - * @return {@code true} if {@code array[i] == target} for some value of {@code - * i} - */ - public static boolean contains(char[] array, char target) { - for (char value : array) { - if (value == target) { - return true; - } - } - return false; - } - - /** - * Returns the index of the first appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code char} values, possibly empty - * @param target a primitive {@code char} value - * @return the least index {@code i} for which {@code array[i] == target}, or - * {@code -1} if no such index exists. - */ - public static int indexOf(char[] array, char target) { - return indexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int indexOf( - char[] array, char target, int start, int end) { - for (int i = start; i < end; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the start position of the first occurrence of the specified {@code - * target} within {@code array}, or {@code -1} if there is no such occurrence. - * - *

More formally, returns the lowest index {@code i} such that {@code - * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly - * the same elements as {@code target}. - * - * @param array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(char[] array, char[] target) { - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Returns the index of the last appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code char} values, possibly empty - * @param target a primitive {@code char} value - * @return the greatest index {@code i} for which {@code array[i] == target}, - * or {@code -1} if no such index exists. - */ - public static int lastIndexOf(char[] array, char target) { - return lastIndexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int lastIndexOf( - char[] array, char target, int start, int end) { - for (int i = end - 1; i >= start; i--) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the least value present in {@code array}. - * - * @param array a nonempty array of {@code char} values - * @return the value present in {@code array} that is less than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static char min(char... array) { - char min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - return min; - } - - /** - * Returns the greatest value present in {@code array}. - * - * @param array a nonempty array of {@code char} values - * @return the value present in {@code array} that is greater than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static char max(char... array) { - char max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - return max; - } - - /** - * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new char[] {a, b}, new char[] {}, new - * char[] {c}} returns the array {@code {a, b, c}}. - * - * @param arrays zero or more {@code char} arrays - * @return a single array containing all the values from the source arrays, in - * order - */ - public static char[] concat(char[]... arrays) { - int length = 0; - for (char[] array : arrays) { - length += array.length; - } - char[] result = new char[length]; - int pos = 0; - for (char[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; - } - return result; - } - - /** - * Returns a big-endian representation of {@code value} in a 2-element byte - * array; equivalent to {@code - * ByteBuffer.allocate(2).putChar(value).array()}. For example, the input - * value {@code '\\u5432'} would yield the byte array {@code {0x54, 0x32}}. - * - */ - public static byte[] toByteArray(char value) { - return new byte[] { - (byte) (value >> 8), - (byte) value}; - } - - /** - * Returns the {@code char} value whose big-endian representation is - * stored in the first 2 bytes of {@code bytes}; equivalent to {@code - * ByteBuffer.wrap(bytes).getChar()}. For example, the input byte array - * {@code {0x54, 0x32}} would yield the {@code char} value {@code '\\u5432'}. - * - *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that - * library exposes much more flexibility at little cost in readability. - * - * @throws IllegalArgumentException if {@code bytes} has fewer than 2 - * elements - */ - public static char fromByteArray(byte[] bytes) { - return fromBytes(bytes[0], bytes[1]); - } - - /** - * Returns the {@code char} value whose byte representation is the given 2 - * bytes, in big-endian order; equivalent to {@code Chars.fromByteArray(new - * byte[] {b1, b2})}. - * - * @since 7.0 - */ - public static char fromBytes(byte b1, byte b2) { - return (char) ((b1 << 8) | (b2 & 0xFF)); - } - - /** - * Returns an array containing the same values as {@code array}, but - * guaranteed to be of a specified minimum length. If {@code array} already - * has a length of at least {@code minLength}, it is returned directly. - * Otherwise, a new array of size {@code minLength + padding} is returned, - * containing the values of {@code array}, and zeroes in the remaining places. - * - * @param array the source array - * @param minLength the minimum length the returned array must guarantee - * @param padding an extra amount to "grow" the array by if growth is - * necessary - * @throws IllegalArgumentException if {@code minLength} or {@code padding} is - * negative - * @return an array containing the values of {@code array}, with guaranteed - * minimum length {@code minLength} - */ - public static char[] ensureCapacity( - char[] array, int minLength, int padding) { - return (array.length < minLength) - ? copyOf(array, minLength + padding) - : array; - } - - // Arrays.copyOf() requires Java 6 - private static char[] copyOf(char[] original, int length) { - char[] copy = new char[length]; - System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); - return copy; - } - - /** - * Returns a string containing the supplied {@code char} values separated - * by {@code separator}. For example, {@code join("-", '1', '2', '3')} returns - * the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in - * the resulting string (but not at the start or end) - * @param array an array of {@code char} values, possibly empty - */ - public static String join(String separator, char... array) { - int len = array.length; - if (len == 0) { - return ""; - } - - StringBuilder builder - = new StringBuilder(len + separator.length() * (len - 1)); - builder.append(array[0]); - for (int i = 1; i < len; i++) { - builder.append(separator).append(array[i]); - } - return builder.toString(); - } - - /** - * Returns a comparator that compares two {@code char} arrays - * lexicographically. That is, it compares, using {@link - * #compare(char, char)}), the first pair of values that follow any - * common prefix, or when one array is a prefix of the other, treats the - * shorter array as the lesser. For example, - * {@code [] < ['a'] < ['a', 'b'] < ['b']}. - * - *

The returned comparator is inconsistent with {@link - * Object#equals(Object)} (since arrays support only identity equality), but - * it is consistent with {@link java.util.Arrays#equals(char[], char[])}. - * - * @see - * Lexicographical order article at Wikipedia - * @since 2.0 - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - private enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(char[] left, char[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - int result = Chars.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - /** - * Copies a collection of {@code Character} instances into a new array of - * primitive {@code char} values. - * - *

Elements are copied from the argument collection as if by {@code - * collection.toArray()}. Calling this method is as thread-safe as calling - * that method. - * - * @param collection a collection of {@code Character} objects - * @return an array containing the same values as {@code collection}, in the - * same order, converted to primitives - * @throws NullPointerException if {@code collection} or any of its elements - * is null - */ - public static char[] toArray(Collection collection) { - if (collection instanceof CharArrayAsList) { - return ((CharArrayAsList) collection).toCharArray(); - } - - Object[] boxedArray = collection.toArray(); - int len = boxedArray.length; - char[] array = new char[len]; - for (int i = 0; i < len; i++) { - // checkNotNull for GWT (do not optimize) - array[i] = (Character) boxedArray[i]; - } - return array; - } - - /** - * Returns a fixed-size list backed by the specified array, similar to {@link - * java.util.Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, - * but any attempt to set a value to {@code null} will result in a {@link - * NullPointerException}. - * - *

The returned list maintains the values, but not the identities, of - * {@code Character} objects written to or read from it. For example, whether - * {@code list.get(0) == list.get(0)} is true for the returned list is - * unspecified. - * - * @param backingArray the array to back the list - * @return a list view of the array - */ - public static List asList(char... backingArray) { - if (backingArray.length == 0) { - return Collections.emptyList(); - } - return new CharArrayAsList(backingArray); - } - - private static class CharArrayAsList extends AbstractList - implements RandomAccess, Serializable { - final char[] array; - final int start; - final int end; - - CharArrayAsList(char[] array) { - this(array, 0, array.length); - } - - CharArrayAsList(char[] array, int start, int end) { - this.array = array; - this.start = start; - this.end = end; - } - - @Override public int size() { - return end - start; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Character get(int index) { - return array[start + index]; - } - - @Override public boolean contains(Object target) { - // Overridden to prevent a ton of boxing - return (target instanceof Character) - && Chars.indexOf(array, (Character) target, start, end) != -1; - } - - @Override public int indexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Character) { - int i = Chars.indexOf(array, (Character) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public int lastIndexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Character) { - int i = Chars.lastIndexOf(array, (Character) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public Character set(int index, Character element) { - char oldValue = array[start + index]; - array[start + index] = element; - return oldValue; - } - - @Override public List subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex == toIndex) { - return Collections.emptyList(); - } - return new CharArrayAsList(array, start + fromIndex, start + toIndex); - } - - @Override public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof CharArrayAsList) { - CharArrayAsList that = (CharArrayAsList) object; - int size = size(); - if (that.size() != size) { - return false; - } - for (int i = 0; i < size; i++) { - if (array[start + i] != that.array[that.start + i]) { - return false; - } - } - return true; - } - return super.equals(object); - } - - @Override public int hashCode() { - int result = 1; - for (int i = start; i < end; i++) { - result = 31 * result + Chars.hashCode(array[i]); - } - return result; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 3); - builder.append('[').append(array[start]); - for (int i = start + 1; i < end; i++) { - builder.append(", ").append(array[i]); - } - return builder.append(']').toString(); - } - - char[] toCharArray() { - // Arrays.copyOfRange() is not available under GWT - int size = size(); - char[] result = new char[size]; - System.arraycopy(array, start, result, 0, size); - return result; - } - - private static final long serialVersionUID = 0; - } -} diff --git a/libtest/src/main/java/z/util/primitives/Ints.java b/libtest/src/main/java/z/util/primitives/Ints.java deleted file mode 100644 index 06bbb506e..000000000 --- a/libtest/src/main/java/z/util/primitives/Ints.java +++ /dev/null @@ -1,562 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.Serializable; -import java.util.*; -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * NOTE: - * Partially form APLed Guava library, but evolves in the Landz independently. - * - * Static utility methods pertaining to {@code int} primitives, that are not - * already found in either {@link Integer} or {@link java.util.Arrays}. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - */ -public final class Ints { - private Ints() {} - - /** - * The number of bytes required to represent a primitive {@code int} - * value. - */ - public static final int BYTES = Integer.SIZE / Byte.SIZE; - - /** - * The largest power of two that can be represented as an {@code int}. - */ - public static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); - - /** - * Returns the {@code int} nearest in value to {@code value}. - * - * @param value any {@code long} value - * @return the same value cast to {@code int} if it is in the range of the - * {@code int} type, {@link Integer#MAX_VALUE} if it is too large, - * or {@link Integer#MIN_VALUE} if it is too small - */ - public static int saturatedCast(long value) { - if (value > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - if (value < Integer.MIN_VALUE) { - return Integer.MIN_VALUE; - } - return (int) value; - } - - /** - * Compares the two specified {@code int} values. The sign of the value - * returned is the same as that of {@code ((Integer) a).compareTo(b)}. - * - * @param a the first {@code int} to compare - * @param b the second {@code int} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive - * value if {@code a} is greater than {@code b}; or zero if they are equal - */ - public static int compare(int a, int b) { - return (a < b) ? -1 : ((a > b) ? 1 : 0); - } - - /** - * Returns {@code true} if {@code target} is present as an element anywhere in - * {@code array}. - * - * @param array an array of {@code int} values, possibly empty - * @param target a primitive {@code int} value - * @return {@code true} if {@code array[i] == target} for some value of {@code - * i} - */ - public static boolean contains(int[] array, int target) { - for (int value : array) { - if (value == target) { - return true; - } - } - return false; - } - - /** - * Returns the index of the first appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code int} values, possibly empty - * @param target a primitive {@code int} value - * @return the least index {@code i} for which {@code array[i] == target}, or - * {@code -1} if no such index exists. - */ - public static int indexOf(int[] array, int target) { - return indexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int indexOf( - int[] array, int target, int start, int end) { - for (int i = start; i < end; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the start position of the first occurrence of the specified {@code - * target} within {@code array}, or {@code -1} if there is no such occurrence. - * - *

More formally, returns the lowest index {@code i} such that {@code - * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly - * the same elements as {@code target}. - * - * @param array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(int[] array, int[] target) { - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Returns the index of the last appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code int} values, possibly empty - * @param target a primitive {@code int} value - * @return the greatest index {@code i} for which {@code array[i] == target}, - * or {@code -1} if no such index exists. - */ - public static int lastIndexOf(int[] array, int target) { - return lastIndexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int lastIndexOf( - int[] array, int target, int start, int end) { - for (int i = end - 1; i >= start; i--) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the least value present in {@code array}. - * - * @param array a nonempty array of {@code int} values - * @return the value present in {@code array} that is less than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static int min(int... array) { - int min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - return min; - } - - /** - * Returns the greatest value present in {@code array}. - * - * @param array a nonempty array of {@code int} values - * @return the value present in {@code array} that is greater than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static int max(int... array) { - int max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - return max; - } - - /** - * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new int[] {a, b}, new int[] {}, new - * int[] {c}} returns the array {@code {a, b, c}}. - * - * @param arrays zero or more {@code int} arrays - * @return a single array containing all the values from the source arrays, in - * order - */ - public static int[] concat(int[]... arrays) { - int length = 0; - for (int[] array : arrays) { - length += array.length; - } - int[] result = new int[length]; - int pos = 0; - for (int[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; - } - return result; - } - - /** - * Returns a big-endian representation of {@code value} in a 4-element byte - * array; equivalent to {@code ByteBuffer.allocate(4).putInt(value).array()}. - * For example, the input value {@code 0x12131415} would yield the byte array - * {@code {0x12, 0x13, 0x14, 0x15}}. - * - *

If you need to convert and concatenate several values (possibly even of - * different types), use a shared {@link java.nio.ByteBuffer} instance. - */ - public static byte[] toByteArray(int value) { - return new byte[] { - (byte) (value >> 24), - (byte) (value >> 16), - (byte) (value >> 8), - (byte) value}; - } - - /** - * Returns the {@code int} value whose byte representation is the given 4 - * bytes, in big-endian order; equivalent to {@code Ints.fromByteArray(new - * byte[] {b1, b2, b3, b4})}. - */ - public static int fromBytes(byte b1, byte b2, byte b3, byte b4) { - return b1 << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF); - } - - /** - * Returns an array containing the same values as {@code array}, but - * guaranteed to be of a specified minimum length. If {@code array} already - * has a length of at least {@code minLength}, it is returned directly. - * Otherwise, a new array of size {@code minLength + padding} is returned, - * containing the values of {@code array}, and zeroes in the remaining places. - * - * @param array the source array - * @param minLength the minimum length the returned array must guarantee - * @param padding an extra amount to "grow" the array by if growth is - * necessary - * @throws IllegalArgumentException if {@code minLength} or {@code padding} is - * negative - * @return an array containing the values of {@code array}, with guaranteed - * minimum length {@code minLength} - */ - public static int[] ensureCapacity( - int[] array, int minLength, int padding) { - return (array.length < minLength) - ? copyOf(array, minLength + padding) - : array; - } - - // Arrays.copyOf() requires Java 6 - private static int[] copyOf(int[] original, int length) { - int[] copy = new int[length]; - System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); - return copy; - } - - /** - * Returns a string containing the supplied {@code int} values separated - * by {@code separator}. For example, {@code join("-", 1, 2, 3)} returns - * the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in - * the resulting string (but not at the start or end) - * @param array an array of {@code int} values, possibly empty - */ - public static String join(String separator, int... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * 5); - builder.append(array[0]); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(array[i]); - } - return builder.toString(); - } - - /** - * Returns {@code true} if {@code x} represents a power of two. - * - *

This differs from {@code Integer.bitCount(x) == 1}, because - * {@code Integer.bitCount(Integer.MIN_VALUE) == 1}, but {@link Integer#MIN_VALUE} is not a power - * of two. - */ - public static boolean isPowerOfTwo(int x) { - return x > 0 & (x & (x - 1)) == 0; - } - - /** - * Returns 1 if {@code x < y} as unsigned integers, and 0 otherwise. Assumes that x - y fits into - * a signed int. The implementation is branch-free, and benchmarks suggest it is measurably (if - * narrowly) faster than the straightforward ternary expression. - */ - public static int lessThanBranchFree(int x, int y) { - // The double negation is optimized away by normal Java, but is necessary for GWT - // to make sure bit twiddling works as expected. - return ~~(x - y) >>> (Integer.SIZE - 1); - } - - /** - * Returns a comparator that compares two {@code int} arrays - * lexicographically. That is, it compares, using {@link - * #compare(int, int)}), the first pair of values that follow any - * common prefix, or when one array is a prefix of the other, treats the - * shorter array as the lesser. For example, {@code [] < [1] < [1, 2] < [2]}. - * - *

The returned comparator is inconsistent with {@link - * Object#equals(Object)} (since arrays support only identity equality), but - * it is consistent with {@link java.util.Arrays#equals(int[], int[])}. - * - * @see - * Lexicographical order article at Wikipedia - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - private enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(int[] left, int[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - int result = Ints.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code int} value in the manner of {@link Number#intValue}. - * - *

Elements are copied from the argument collection as if by {@code - * collection.toArray()}. Calling this method is as thread-safe as calling - * that method. - * - * @param collection a collection of {@code Number} instances - * @return an array containing the same values as {@code collection}, in the - * same order, converted to primitives - * @throws NullPointerException if {@code collection} or any of its elements - * is null - */ - public static int[] toArray(Collection collection) { - if (collection instanceof IntArrayAsList) { - return ((IntArrayAsList) collection).toIntArray(); - } - - Object[] boxedArray = collection.toArray(); - int len = boxedArray.length; - int[] array = new int[len]; - for (int i = 0; i < len; i++) { - array[i] = ((Number)boxedArray[i]).intValue(); - } - return array; - } - - /** - * Returns a fixed-size list backed by the specified array, similar to {@link - * java.util.Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, - * but any attempt to set a value to {@code null} will result in a {@link - * NullPointerException}. - * - *

The returned list maintains the values, but not the identities, of - * {@code Integer} objects written to or read from it. For example, whether - * {@code list.get(0) == list.get(0)} is true for the returned list is - * unspecified. - * - * @param backingArray the array to back the list - * @return a list view of the array - */ - public static List asList(int... backingArray) { - if (backingArray.length == 0) { - return Collections.emptyList(); - } - return new IntArrayAsList(backingArray); - } - - private static class IntArrayAsList extends AbstractList - implements RandomAccess, Serializable { - final int[] array; - final int start; - final int end; - - IntArrayAsList(int[] array) { - this(array, 0, array.length); - } - - IntArrayAsList(int[] array, int start, int end) { - this.array = array; - this.start = start; - this.end = end; - } - - @Override public int size() { - return end - start; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Integer get(int index) { - return array[start + index]; - } - - @Override public boolean contains(Object target) { - // Overridden to prevent a ton of boxing - return (target instanceof Integer) - && Ints.indexOf(array, (Integer) target, start, end) != -1; - } - - @Override public int indexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Integer) { - int i = Ints.indexOf(array, (Integer) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public int lastIndexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Integer) { - int i = Ints.lastIndexOf(array, (Integer) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public Integer set(int index, Integer element) { - int oldValue = array[start + index]; - array[start + index] = element; - return oldValue; - } - - @Override public List subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex == toIndex) { - return Collections.emptyList(); - } - return new IntArrayAsList(array, start + fromIndex, start + toIndex); - } - - @Override public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof IntArrayAsList) { - IntArrayAsList that = (IntArrayAsList) object; - int size = size(); - if (that.size() != size) { - return false; - } - for (int i = 0; i < size; i++) { - if (array[start + i] != that.array[that.start + i]) { - return false; - } - } - return true; - } - return super.equals(object); - } - - @Override public int hashCode() { - int result = 1; - for (int i = start; i < end; i++) { - result = 31 * result + array[i]; - } - return result; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 5); - builder.append('[').append(array[start]); - for (int i = start + 1; i < end; i++) { - builder.append(", ").append(array[i]); - } - return builder.append(']').toString(); - } - - int[] toIntArray() { - // Arrays.copyOfRange() is not available under GWT - int size = size(); - int[] result = new int[size]; - System.arraycopy(array, start, result, 0, size); - return result; - } - - private static final long serialVersionUID = 0; - } - -} diff --git a/libtest/src/main/java/z/util/primitives/Longs.java b/libtest/src/main/java/z/util/primitives/Longs.java deleted file mode 100644 index 65cc90f11..000000000 --- a/libtest/src/main/java/z/util/primitives/Longs.java +++ /dev/null @@ -1,630 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - -import java.io.Serializable; -import java.util.*; -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * NOTE: - * Partially form APLed Guava library, but evolves in the Landz independently. - * - * Static utility methods pertaining to {@code long} primitives, that are not - * already found in either {@link Long} or {@link java.util.Arrays}. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - */ -public final class Longs { - private Longs() {} - - /** - * The number of bytes required to represent a primitive {@code long} - * value. - */ - public static final int BYTES = Long.SIZE / Byte.SIZE; - - /** - * The largest power of two that can be represented as a {@code long}. - */ - public static final long MAX_POWER_OF_TWO = 1L << (Long.SIZE - 2); - - /** - * Compares the two specified {@code long} values. The sign of the value - * returned is the same as that of {@code ((Long) a).compareTo(b)}. - * - * @param a the first {@code long} to compare - * @param b the second {@code long} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive - * value if {@code a} is greater than {@code b}; or zero if they are equal - */ - public static int compare(long a, long b) { - return (a < b) ? -1 : ((a > b) ? 1 : 0); - } - - /** - * Returns {@code true} if {@code target} is present as an element anywhere in - * {@code array}. - * - * @param array an array of {@code long} values, possibly empty - * @param target a primitive {@code long} value - * @return {@code true} if {@code array[i] == target} for some value of {@code - * i} - */ - public static boolean contains(long[] array, long target) { - for (long value : array) { - if (value == target) { - return true; - } - } - return false; - } - - /** - * Returns the index of the first appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code long} values, possibly empty - * @param target a primitive {@code long} value - * @return the least index {@code i} for which {@code array[i] == target}, or - * {@code -1} if no such index exists. - */ - public static int indexOf(long[] array, long target) { - return indexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int indexOf( - long[] array, long target, int start, int end) { - for (int i = start; i < end; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the start position of the first occurrence of the specified {@code - * target} within {@code array}, or {@code -1} if there is no such occurrence. - * - *

More formally, returns the lowest index {@code i} such that {@code - * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly - * the same elements as {@code target}. - * - * @param array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(long[] array, long[] target) { - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Returns the index of the last appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code long} values, possibly empty - * @param target a primitive {@code long} value - * @return the greatest index {@code i} for which {@code array[i] == target}, - * or {@code -1} if no such index exists. - */ - public static int lastIndexOf(long[] array, long target) { - return lastIndexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int lastIndexOf( - long[] array, long target, int start, int end) { - for (int i = end - 1; i >= start; i--) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the least value present in {@code array}. - * - * @param array a nonempty array of {@code long} values - * @return the value present in {@code array} that is less than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static long min(long... array) { - long min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - return min; - } - - /** - * Returns the greatest value present in {@code array}. - * - * @param array a nonempty array of {@code long} values - * @return the value present in {@code array} that is greater than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static long max(long... array) { - long max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - return max; - } - - /** - * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new long[] {a, b}, new long[] {}, new - * long[] {c}} returns the array {@code {a, b, c}}. - * - * @param arrays zero or more {@code long} arrays - * @return a single array containing all the values from the source arrays, in - * order - */ - public static long[] concat(long[]... arrays) { - int length = 0; - for (long[] array : arrays) { - length += array.length; - } - long[] result = new long[length]; - int pos = 0; - for (long[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; - } - return result; - } - - /** - * Returns a big-endian representation of {@code value} in an 8-element byte - * array; equivalent to {@code ByteBuffer.allocate(8).putLong(value).array()}. - * For example, the input value {@code 0x1213141516171819L} would yield the - * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. - * - *

If you need to convert and concatenate several values (possibly even of - * different types), use a shared {@link java.nio.ByteBuffer} instance. - */ - public static byte[] toByteArray(long value) { - // Note that this code needs to stay compatible with GWT, which has known - // bugs when narrowing byte casts of long values occur. - byte[] result = new byte[8]; - for (int i = 7; i >= 0; i--) { - result[i] = (byte) (value & 0xffL); - value >>= 8; - } - return result; - } - - /** - * Returns the {@code long} value whose big-endian representation is - * stored in the first 8 bytes of {@code bytes}; equivalent to {@code - * ByteBuffer.wrap(bytes).getLong()}. For example, the input byte array - * {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}} would yield the - * {@code long} value {@code 0x1213141516171819L}. - * - *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that - * library exposes much more flexibility at little cost in readability. - * - * @throws IllegalArgumentException if {@code bytes} has fewer than 8 - * elements - */ - public static long fromByteArray(byte[] bytes) { - return fromBytes(bytes[0], bytes[1], bytes[2], bytes[3], - bytes[4], bytes[5], bytes[6], bytes[7]) ; - } - - /** - * Returns the {@code long} value whose byte representation is the given 8 - * bytes, in big-endian order; equivalent to {@code Longs.fromByteArray(new - * byte[] {b1, b2, b3, b4, b5, b6, b7, b8})}. - * - * @since 7.0 - */ - public static long fromBytes(byte b1, byte b2, byte b3, byte b4, - byte b5, byte b6, byte b7, byte b8) { - return (b1 & 0xFFL) << 56 - | (b2 & 0xFFL) << 48 - | (b3 & 0xFFL) << 40 - | (b4 & 0xFFL) << 32 - | (b5 & 0xFFL) << 24 - | (b6 & 0xFFL) << 16 - | (b7 & 0xFFL) << 8 - | (b8 & 0xFFL); - } - - /** - * Parses the specified string as a signed decimal long value. The ASCII - * character {@code '-'} ('\u002D') is recognized as the - * minus sign. - * - *

Unlike {@link Long#parseLong(String)}, this method returns - * {@code null} instead of throwing an exception if parsing fails. - * - *

Note that strings prefixed with ASCII {@code '+'} are rejected, even - * under JDK 7, despite the change to {@link Long#parseLong(String)} for - * that version. - * - * @param string the string representation of a long value - * @return the long value represented by {@code string}, or {@code null} if - * {@code string} has a length of zero or cannot be parsed as a long - * value - */ - public static Long tryParse(String string) { - if (string.isEmpty()) { - return null; - } - boolean negative = string.charAt(0) == '-'; - int index = negative ? 1 : 0; - if (index == string.length()) { - return null; - } - int digit = string.charAt(index++) - '0'; - if (digit < 0 || digit > 9) { - return null; - } - long accum = -digit; - while (index < string.length()) { - digit = string.charAt(index++) - '0'; - if (digit < 0 || digit > 9 || accum < Long.MIN_VALUE / 10) { - return null; - } - accum *= 10; - if (accum < Long.MIN_VALUE + digit) { - return null; - } - accum -= digit; - } - - if (negative) { - return accum; - } else if (accum == Long.MIN_VALUE) { - return null; - } else { - return -accum; - } - } - - /** - * Returns an array containing the same values as {@code array}, but - * guaranteed to be of a specified minimum length. If {@code array} already - * has a length of at least {@code minLength}, it is returned directly. - * Otherwise, a new array of size {@code minLength + padding} is returned, - * containing the values of {@code array}, and zeroes in the remaining places. - * - * @param array the source array - * @param minLength the minimum length the returned array must guarantee - * @param padding an extra amount to "grow" the array by if growth is - * necessary - * @throws IllegalArgumentException if {@code minLength} or {@code padding} is - * negative - * @return an array containing the values of {@code array}, with guaranteed - * minimum length {@code minLength} - */ - public static long[] ensureCapacity( - long[] array, int minLength, int padding) { - return (array.length < minLength) - ? copyOf(array, minLength + padding) - : array; - } - - // Arrays.copyOf() requires Java 6 - private static long[] copyOf(long[] original, int length) { - long[] copy = new long[length]; - System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); - return copy; - } - - /** - * Returns a string containing the supplied {@code long} values separated - * by {@code separator}. For example, {@code join("-", 1L, 2L, 3L)} returns - * the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in - * the resulting string (but not at the start or end) - * @param array an array of {@code long} values, possibly empty - */ - public static String join(String separator, long... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * 10); - builder.append(array[0]); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(array[i]); - } - return builder.toString(); - } - - /** - * Returns {@code true} if {@code x} represents a power of two. - * - *

This differs from {@code Long.bitCount(x) == 1}, because - * {@code Long.bitCount(Long.MIN_VALUE) == 1}, but {@link Long#MIN_VALUE} is not a power of two. - */ - public static boolean isPowerOfTwo(long x) { - return x > 0 & (x & (x - 1)) == 0; - } - - /** - * Returns 1 if {@code x < y} as unsigned longs, and 0 otherwise. Assumes that x - y fits into a - * signed long. The implementation is branch-free, and benchmarks suggest it is measurably - * faster than the straightforward ternary expression. - */ - public static int lessThanBranchFree(long x, long y) { - // Returns the sign bit of x - y. - return (int) (~~(x - y) >>> (Long.SIZE - 1)); - } - - /** - * Returns a comparator that compares two {@code long} arrays - * lexicographically. That is, it compares, using {@link - * #compare(long, long)}), the first pair of values that follow any - * common prefix, or when one array is a prefix of the other, treats the - * shorter array as the lesser. For example, - * {@code [] < [1L] < [1L, 2L] < [2L]}. - * - *

The returned comparator is inconsistent with {@link - * Object#equals(Object)} (since arrays support only identity equality), but - * it is consistent with {@link java.util.Arrays#equals(long[], long[])}. - * - * @see - * Lexicographical order article at Wikipedia - * @since 2.0 - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - private enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(long[] left, long[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - int result = Longs.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code long} value in the manner of {@link Number#longValue}. - * - *

Elements are copied from the argument collection as if by {@code - * collection.toArray()}. Calling this method is as thread-safe as calling - * that method. - * - * @param collection a collection of {@code Number} instances - * @return an array containing the same values as {@code collection}, in the - * same order, converted to primitives - * @throws NullPointerException if {@code collection} or any of its elements - * is null - * @since 1.0 (parameter was {@code Collection} before 12.0) - */ - public static long[] toArray(Collection collection) { - if (collection instanceof LongArrayAsList) { - return ((LongArrayAsList) collection).toLongArray(); - } - - Object[] boxedArray = collection.toArray(); - int len = boxedArray.length; - long[] array = new long[len]; - for (int i = 0; i < len; i++) { - // checkNotNull for GWT (do not optimize) - array[i] = ((Number) boxedArray[i]).longValue(); - } - return array; - } - - /** - * Returns a fixed-size list backed by the specified array, similar to {@link - * java.util.Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, - * but any attempt to set a value to {@code null} will result in a {@link - * NullPointerException}. - * - *

The returned list maintains the values, but not the identities, of - * {@code Long} objects written to or read from it. For example, whether - * {@code list.get(0) == list.get(0)} is true for the returned list is - * unspecified. - * - * @param backingArray the array to back the list - * @return a list view of the array - */ - public static List asList(long... backingArray) { - if (backingArray.length == 0) { - return Collections.emptyList(); - } - return new LongArrayAsList(backingArray); - } - - private static class LongArrayAsList extends AbstractList - implements RandomAccess, Serializable { - final long[] array; - final int start; - final int end; - - LongArrayAsList(long[] array) { - this(array, 0, array.length); - } - - LongArrayAsList(long[] array, int start, int end) { - this.array = array; - this.start = start; - this.end = end; - } - - @Override public int size() { - return end - start; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Long get(int index) { - return array[start + index]; - } - - @Override public boolean contains(Object target) { - // Overridden to prevent a ton of boxing - return (target instanceof Long) - && Longs.indexOf(array, (Long) target, start, end) != -1; - } - - @Override public int indexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Long) { - int i = Longs.indexOf(array, (Long) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public int lastIndexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Long) { - int i = Longs.lastIndexOf(array, (Long) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public Long set(int index, Long element) { - long oldValue = array[start + index]; - array[start + index] =element; - return oldValue; - } - - @Override public List subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex == toIndex) { - return Collections.emptyList(); - } - return new LongArrayAsList(array, start + fromIndex, start + toIndex); - } - - @Override public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof LongArrayAsList) { - LongArrayAsList that = (LongArrayAsList) object; - int size = size(); - if (that.size() != size) { - return false; - } - for (int i = 0; i < size; i++) { - if (array[start + i] != that.array[that.start + i]) { - return false; - } - } - return true; - } - return super.equals(object); - } - - @Override public int hashCode() { - int result = 1; - for (int i = start; i < end; i++) { - result = 31 * result + (int) (array[i] ^ (array[i] >>> 32)); - } - return result; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 10); - builder.append('[').append(array[start]); - for (int i = start + 1; i < end; i++) { - builder.append(", ").append(array[i]); - } - return builder.append(']').toString(); - } - - long[] toLongArray() { - // Arrays.copyOfRange() is not available under GWT - int size = size(); - long[] result = new long[size]; - System.arraycopy(array, start, result, 0, size); - return result; - } - - private static final long serialVersionUID = 0; - } -} diff --git a/libtest/src/main/java/z/util/primitives/Primitives.java b/libtest/src/main/java/z/util/primitives/Primitives.java deleted file mode 100644 index ea1ff57bb..000000000 --- a/libtest/src/main/java/z/util/primitives/Primitives.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * NOTE: - * Partially form APLed Guava library, but evolves in the Landz independently. - * Contains static utility methods pertaining to primitive types and their - * corresponding wrapper types. - * - * @author Kevin Bourrillion - */ -public final class Primitives { - private Primitives() {} - - /** A map from primitive types to their corresponding wrapper types. */ - private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; - - /** A map from wrapper types to their corresponding primitive types. */ - private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; - - // Sad that we can't use a BiMap. :( - - static { - Map, Class> primToWrap = new HashMap, Class>(16); - Map, Class> wrapToPrim = new HashMap, Class>(16); - - add(primToWrap, wrapToPrim, boolean.class, Boolean.class); - add(primToWrap, wrapToPrim, byte.class, Byte.class); - add(primToWrap, wrapToPrim, char.class, Character.class); - add(primToWrap, wrapToPrim, double.class, Double.class); - add(primToWrap, wrapToPrim, float.class, Float.class); - add(primToWrap, wrapToPrim, int.class, Integer.class); - add(primToWrap, wrapToPrim, long.class, Long.class); - add(primToWrap, wrapToPrim, short.class, Short.class); - add(primToWrap, wrapToPrim, void.class, Void.class); - - PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); - WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); - } - - private static void add(Map, Class> forward, - Map, Class> backward, Class key, Class value) { - forward.put(key, value); - backward.put(value, key); - } - - /** - * Returns an immutable set of all nine primitive types (including {@code - * void}). Note that a simpler way to test whether a {@code Class} instance - * is a member of this set is to call {@link Class#isPrimitive}. - */ - public static Set> allPrimitiveTypes() { - return PRIMITIVE_TO_WRAPPER_TYPE.keySet(); - } - - /** - * Returns an immutable set of all nine primitive-wrapper types (including - * {@link Void}). - */ - public static Set> allWrapperTypes() { - return WRAPPER_TO_PRIMITIVE_TYPE.keySet(); - } - - /** - * Returns {@code true} if {@code type} is one of the nine - * primitive-wrapper types, such as {@link Integer}. - * - * @see Class#isPrimitive - */ - public static boolean isWrapperType(Class type) { - return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(type); - } - - /** - * Returns the corresponding wrapper type of {@code type} if it is a primitive - * type; otherwise returns {@code type} itself. Idempotent. - *

-   *     wrap(int.class) == Integer.class
-   *     wrap(Integer.class) == Integer.class
-   *     wrap(String.class) == String.class
-   * 
- */ - public static Class wrap(Class type) { - // cast is safe: long.class and Long.class are both of type Class - @SuppressWarnings("unchecked") - Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(type); - return (wrapped == null) ? type : wrapped; - } - - /** - * Returns the corresponding primitive type of {@code type} if it is a - * wrapper type; otherwise returns {@code type} itself. Idempotent. - *
-   *     unwrap(Integer.class) == int.class
-   *     unwrap(int.class) == int.class
-   *     unwrap(String.class) == String.class
-   * 
- */ - public static Class unwrap(Class type) { - // cast is safe: long.class and Long.class are both of type Class - @SuppressWarnings("unchecked") - Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE.get(type); - return (unwrapped == null) ? type : unwrapped; - } -} diff --git a/libtest/src/main/java/z/util/primitives/Shorts.java b/libtest/src/main/java/z/util/primitives/Shorts.java deleted file mode 100644 index e6c8fd91d..000000000 --- a/libtest/src/main/java/z/util/primitives/Shorts.java +++ /dev/null @@ -1,594 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.io.Serializable; -import java.util.*; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code short} primitives, that are not - * already found in either {@link Short} or {@link java.util.Arrays}. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - * @since 1.0 - */ -public final class Shorts { - private Shorts() {} - - /** - * The number of bytes required to represent a primitive {@code short} - * value. - */ - public static final int BYTES = Short.SIZE / Byte.SIZE; - - /** - * The largest power of two that can be represented as a {@code short}. - * - * @since 10.0 - */ - public static final short MAX_POWER_OF_TWO = 1 << (Short.SIZE - 2); - - /** - * Returns a hash code for {@code value}; equal to the result of invoking - * {@code ((Short) value).hashCode()}. - * - * @param value a primitive {@code short} value - * @return a hash code for the value - */ - public static int hashCode(short value) { - return value; - } - - /** - * Returns the {@code short} value that is equal to {@code value}, if - * possible. - * - * @param value any value in the range of the {@code short} type - * @return the {@code short} value that equals {@code value} - * @throws IllegalArgumentException if {@code value} is greater than {@link - * Short#MAX_VALUE} or less than {@link Short#MIN_VALUE} - */ - public static short checkedCast(long value) { - short result = (short) value; - if (result != value) { - // don't use checkArgument here, to avoid boxing - throw new IllegalArgumentException("Out of range: " + value); - } - return result; - } - - /** - * Returns the {@code short} nearest in value to {@code value}. - * - * @param value any {@code long} value - * @return the same value cast to {@code short} if it is in the range of the - * {@code short} type, {@link Short#MAX_VALUE} if it is too large, - * or {@link Short#MIN_VALUE} if it is too small - */ - public static short saturatedCast(long value) { - if (value > Short.MAX_VALUE) { - return Short.MAX_VALUE; - } - if (value < Short.MIN_VALUE) { - return Short.MIN_VALUE; - } - return (short) value; - } - - /** - * Compares the two specified {@code short} values. The sign of the value - * returned is the same as that of {@code ((Short) a).compareTo(b)}. - * - *

Note: projects using JDK 7 or later should use the equivalent - * {@link Short#compare} method instead. - * - * @param a the first {@code short} to compare - * @param b the second {@code short} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive - * value if {@code a} is greater than {@code b}; or zero if they are equal - */ - // TODO(kevinb): if JDK 6 ever becomes a non-concern, remove this - public static int compare(short a, short b) { - return a - b; // safe due to restricted range - } - - /** - * Returns {@code true} if {@code target} is present as an element anywhere in - * {@code array}. - * - * @param array an array of {@code short} values, possibly empty - * @param target a primitive {@code short} value - * @return {@code true} if {@code array[i] == target} for some value of {@code - * i} - */ - public static boolean contains(short[] array, short target) { - for (short value : array) { - if (value == target) { - return true; - } - } - return false; - } - - /** - * Returns the index of the first appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code short} values, possibly empty - * @param target a primitive {@code short} value - * @return the least index {@code i} for which {@code array[i] == target}, or - * {@code -1} if no such index exists. - */ - public static int indexOf(short[] array, short target) { - return indexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int indexOf( - short[] array, short target, int start, int end) { - for (int i = start; i < end; i++) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the start position of the first occurrence of the specified {@code - * target} within {@code array}, or {@code -1} if there is no such occurrence. - * - *

More formally, returns the lowest index {@code i} such that {@code - * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly - * the same elements as {@code target}. - * - * @param array the array to search for the sequence {@code target} - * @param target the array to search for as a sub-sequence of {@code array} - */ - public static int indexOf(short[] array, short[] target) { - if (target.length == 0) { - return 0; - } - - outer: - for (int i = 0; i < array.length - target.length + 1; i++) { - for (int j = 0; j < target.length; j++) { - if (array[i + j] != target[j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Returns the index of the last appearance of the value {@code target} in - * {@code array}. - * - * @param array an array of {@code short} values, possibly empty - * @param target a primitive {@code short} value - * @return the greatest index {@code i} for which {@code array[i] == target}, - * or {@code -1} if no such index exists. - */ - public static int lastIndexOf(short[] array, short target) { - return lastIndexOf(array, target, 0, array.length); - } - - // TODO(kevinb): consider making this public - private static int lastIndexOf( - short[] array, short target, int start, int end) { - for (int i = end - 1; i >= start; i--) { - if (array[i] == target) { - return i; - } - } - return -1; - } - - /** - * Returns the least value present in {@code array}. - * - * @param array a nonempty array of {@code short} values - * @return the value present in {@code array} that is less than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static short min(short... array) { - short min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - return min; - } - - /** - * Returns the greatest value present in {@code array}. - * - * @param array a nonempty array of {@code short} values - * @return the value present in {@code array} that is greater than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static short max(short... array) { - short max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - return max; - } - - /** - * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new short[] {a, b}, new short[] {}, new - * short[] {c}} returns the array {@code {a, b, c}}. - * - * @param arrays zero or more {@code short} arrays - * @return a single array containing all the values from the source arrays, in - * order - */ - public static short[] concat(short[]... arrays) { - int length = 0; - for (short[] array : arrays) { - length += array.length; - } - short[] result = new short[length]; - int pos = 0; - for (short[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; - } - return result; - } - - /** - * Returns a big-endian representation of {@code value} in a 2-element byte - * array; equivalent to {@code - * ByteBuffer.allocate(2).putShort(value).array()}. For example, the input - * value {@code (short) 0x1234} would yield the byte array {@code {0x12, - * 0x34}}. - * - */ - public static byte[] toByteArray(short value) { - return new byte[] { - (byte) (value >> 8), - (byte) value}; - } - - /** - * Returns the {@code short} value whose big-endian representation is - * stored in the first 2 bytes of {@code bytes}; equivalent to {@code - * ByteBuffer.wrap(bytes).getShort()}. For example, the input byte array - * {@code {0x54, 0x32}} would yield the {@code short} value {@code 0x5432}. - * - *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that - * library exposes much more flexibility at little cost in readability. - * - * @throws IllegalArgumentException if {@code bytes} has fewer than 2 - * elements - */ - public static short fromByteArray(byte[] bytes) { - return fromBytes(bytes[0], bytes[1]); - } - - /** - * Returns the {@code short} value whose byte representation is the given 2 - * bytes, in big-endian order; equivalent to {@code Shorts.fromByteArray(new - * byte[] {b1, b2})}. - * - * @since 7.0 - */ - public static short fromBytes(byte b1, byte b2) { - return (short) ((b1 << 8) | (b2 & 0xFF)); - } - - - /** - * Returns an array containing the same values as {@code array}, but - * guaranteed to be of a specified minimum length. If {@code array} already - * has a length of at least {@code minLength}, it is returned directly. - * Otherwise, a new array of size {@code minLength + padding} is returned, - * containing the values of {@code array}, and zeroes in the remaining places. - * - * @param array the source array - * @param minLength the minimum length the returned array must guarantee - * @param padding an extra amount to "grow" the array by if growth is - * necessary - * @throws IllegalArgumentException if {@code minLength} or {@code padding} is - * negative - * @return an array containing the values of {@code array}, with guaranteed - * minimum length {@code minLength} - */ - public static short[] ensureCapacity( - short[] array, int minLength, int padding) { - return (array.length < minLength) - ? copyOf(array, minLength + padding) - : array; - } - - // Arrays.copyOf() requires Java 6 - private static short[] copyOf(short[] original, int length) { - short[] copy = new short[length]; - System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); - return copy; - } - - /** - * Returns a string containing the supplied {@code short} values separated - * by {@code separator}. For example, {@code join("-", (short) 1, (short) 2, - * (short) 3)} returns the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in - * the resulting string (but not at the start or end) - * @param array an array of {@code short} values, possibly empty - */ - public static String join(String separator, short... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * 6); - builder.append(array[0]); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(array[i]); - } - return builder.toString(); - } - - /** - * Returns a comparator that compares two {@code short} arrays - * lexicographically. That is, it compares, using {@link - * #compare(short, short)}), the first pair of values that follow any - * common prefix, or when one array is a prefix of the other, treats the - * shorter array as the lesser. For example, {@code [] < [(short) 1] < - * [(short) 1, (short) 2] < [(short) 2]}. - * - *

The returned comparator is inconsistent with {@link - * Object#equals(Object)} (since arrays support only identity equality), but - * it is consistent with {@link java.util.Arrays#equals(short[], short[])}. - * - * @see - * Lexicographical order article at Wikipedia - * @since 2.0 - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - private enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(short[] left, short[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - int result = Shorts.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code short} value in the manner of {@link Number#shortValue}. - * - *

Elements are copied from the argument collection as if by {@code - * collection.toArray()}. Calling this method is as thread-safe as calling - * that method. - * - * @param collection a collection of {@code Number} instances - * @return an array containing the same values as {@code collection}, in the - * same order, converted to primitives - * @throws NullPointerException if {@code collection} or any of its elements - * is null - * @since 1.0 (parameter was {@code Collection} before 12.0) - */ - public static short[] toArray(Collection collection) { - if (collection instanceof ShortArrayAsList) { - return ((ShortArrayAsList) collection).toShortArray(); - } - - Object[] boxedArray = collection.toArray(); - int len = boxedArray.length; - short[] array = new short[len]; - for (int i = 0; i < len; i++) { - array[i] = ((Number) boxedArray[i]).shortValue(); - } - return array; - } - - /** - * Returns a fixed-size list backed by the specified array, similar to {@link - * java.util.Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, - * but any attempt to set a value to {@code null} will result in a {@link - * NullPointerException}. - * - *

The returned list maintains the values, but not the identities, of - * {@code Short} objects written to or read from it. For example, whether - * {@code list.get(0) == list.get(0)} is true for the returned list is - * unspecified. - * - * @param backingArray the array to back the list - * @return a list view of the array - */ - public static List asList(short... backingArray) { - if (backingArray.length == 0) { - return Collections.emptyList(); - } - return new ShortArrayAsList(backingArray); - } - - private static class ShortArrayAsList extends AbstractList - implements RandomAccess, Serializable { - final short[] array; - final int start; - final int end; - - ShortArrayAsList(short[] array) { - this(array, 0, array.length); - } - - ShortArrayAsList(short[] array, int start, int end) { - this.array = array; - this.start = start; - this.end = end; - } - - @Override public int size() { - return end - start; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Short get(int index) { - return array[start + index]; - } - - @Override public boolean contains(Object target) { - // Overridden to prevent a ton of boxing - return (target instanceof Short) - && Shorts.indexOf(array, (Short) target, start, end) != -1; - } - - @Override public int indexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Short) { - int i = Shorts.indexOf(array, (Short) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public int lastIndexOf(Object target) { - // Overridden to prevent a ton of boxing - if (target instanceof Short) { - int i = Shorts.lastIndexOf(array, (Short) target, start, end); - if (i >= 0) { - return i - start; - } - } - return -1; - } - - @Override public Short set(int index, Short element) { - short oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = element; - return oldValue; - } - - @Override public List subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex == toIndex) { - return Collections.emptyList(); - } - return new ShortArrayAsList(array, start + fromIndex, start + toIndex); - } - - @Override public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof ShortArrayAsList) { - ShortArrayAsList that = (ShortArrayAsList) object; - int size = size(); - if (that.size() != size) { - return false; - } - for (int i = 0; i < size; i++) { - if (array[start + i] != that.array[that.start + i]) { - return false; - } - } - return true; - } - return super.equals(object); - } - - @Override public int hashCode() { - int result = 1; - for (int i = start; i < end; i++) { - result = 31 * result + Shorts.hashCode(array[i]); - } - return result; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 6); - builder.append('[').append(array[start]); - for (int i = start + 1; i < end; i++) { - builder.append(", ").append(array[i]); - } - return builder.append(']').toString(); - } - - short[] toShortArray() { - // Arrays.copyOfRange() is not available under GWT - int size = size(); - short[] result = new short[size]; - System.arraycopy(array, start, result, 0, size); - return result; - } - - private static final long serialVersionUID = 0; - } -} diff --git a/libtest/src/main/java/z/util/primitives/UnsignedBytes.java b/libtest/src/main/java/z/util/primitives/UnsignedBytes.java deleted file mode 100644 index 2dd67db7d..000000000 --- a/libtest/src/main/java/z/util/primitives/UnsignedBytes.java +++ /dev/null @@ -1,450 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import sun.misc.Unsafe; - -import java.nio.ByteOrder; -import java.util.Comparator; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code byte} primitives that interpret - * values as unsigned (that is, any negative value {@code b} is treated - * as the positive value {@code 256 + b}). The methods for - * which signedness is not an issue are in {@link Bytes}. - * - *

See the Guava User Guide article on - * primitive utilities. - * - * @author Kevin Bourrillion - * @author Martin Buchholz - * @author Hiroshi Yamauchi - * @author Louis Wasserman - * @since 1.0 - */ -public final class UnsignedBytes { - private UnsignedBytes() {} - - /** - * The largest power of two that can be represented as an unsigned {@code - * byte}. - * - * @since 10.0 - */ - public static final byte MAX_POWER_OF_TWO = (byte) 0x80; - - /** - * The largest value that fits into an unsigned byte. - * - * @since 13.0 - */ - public static final byte MAX_VALUE = (byte) 0xFF; - - private static final int UNSIGNED_MASK = 0xFF; - - /** - * Returns the value of the given byte as an integer, when treated as - * unsigned. That is, returns {@code value + 256} if {@code value} is - * negative; {@code value} itself otherwise. - * - * @since 6.0 - */ - public static int toInt(byte value) { - return value & UNSIGNED_MASK; - } - - /** - * Returns the {@code byte} value that, when treated as unsigned, is equal to - * {@code value}, if possible. - * - * @param value a value between 0 and 255 inclusive - * @return the {@code byte} value that, when treated as unsigned, equals - * {@code value} - * @throws IllegalArgumentException if {@code value} is negative or greater - * than 255 - */ - public static byte checkedCast(long value) { - if ((value >> Byte.SIZE) != 0) { - // don't use checkArgument here, to avoid boxing - throw new IllegalArgumentException("Out of range: " + value); - } - return (byte) value; - } - - /** - * Returns the {@code byte} value that, when treated as unsigned, is nearest - * in value to {@code value}. - * - * @param value any {@code long} value - * @return {@code (byte) 255} if {@code value >= 255}, {@code (byte) 0} if - * {@code value <= 0}, and {@code value} cast to {@code byte} otherwise - */ - public static byte saturatedCast(long value) { - if (value > toInt(MAX_VALUE)) { - return MAX_VALUE; // -1 - } - if (value < 0) { - return (byte) 0; - } - return (byte) value; - } - - /** - * Compares the two specified {@code byte} values, treating them as unsigned - * values between 0 and 255 inclusive. For example, {@code (byte) -127} is - * considered greater than {@code (byte) 127} because it is seen as having - * the value of positive {@code 129}. - * - * @param a the first {@code byte} to compare - * @param b the second {@code byte} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive - * value if {@code a} is greater than {@code b}; or zero if they are equal - */ - public static int compare(byte a, byte b) { - return toInt(a) - toInt(b); - } - - /** - * Returns the least value present in {@code array}. - * - * @param array a nonempty array of {@code byte} values - * @return the value present in {@code array} that is less than or equal to - * every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static byte min(byte... array) { - int min = toInt(array[0]); - for (int i = 1; i < array.length; i++) { - int next = toInt(array[i]); - if (next < min) { - min = next; - } - } - return (byte) min; - } - - /** - * Returns the greatest value present in {@code array}. - * - * @param array a nonempty array of {@code byte} values - * @return the value present in {@code array} that is greater than or equal - * to every other value in the array - * @throws IllegalArgumentException if {@code array} is empty - */ - public static byte max(byte... array) { - int max = toInt(array[0]); - for (int i = 1; i < array.length; i++) { - int next = toInt(array[i]); - if (next > max) { - max = next; - } - } - return (byte) max; - } - - /** - * Returns a string representation of x, where x is treated as unsigned. - * - * @since 13.0 - */ - public static String toString(byte x) { - return toString(x, 10); - } - - /** - * Returns a string representation of {@code x} for the given radix, where {@code x} is treated - * as unsigned. - * - * @param x the value to convert to a string. - * @param radix the radix to use while working with {@code x} - * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - * @since 13.0 - */ - public static String toString(byte x, int radix) { - // Benchmarks indicate this is probably not worth optimizing. - return Integer.toString(toInt(x), radix); - } - - /** - * Returns the unsigned {@code byte} value represented by the given decimal string. - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} - * value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Byte#parseByte(String)}) - * @since 13.0 - */ - public static byte parseUnsignedByte(String string) { - return parseUnsignedByte(string, 10); - } - - /** - * Returns the unsigned {@code byte} value represented by a string with the given radix. - * - * @param string the string containing the unsigned {@code byte} representation to be parsed. - * @param radix the radix to use while parsing {@code string} - * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} - * with the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Byte#parseByte(String)}) - * @since 13.0 - */ - public static byte parseUnsignedByte(String string, int radix) { - int parse = Integer.parseInt(string, radix); - // We need to throw a NumberFormatException, so we have to duplicate checkedCast. =( - if (parse >> Byte.SIZE == 0) { - return (byte) parse; - } else { - throw new NumberFormatException("out of range: " + parse); - } - } - - /** - * Returns a string containing the supplied {@code byte} values separated by - * {@code separator}. For example, {@code join(":", (byte) 1, (byte) 2, - * (byte) 255)} returns the string {@code "1:2:255"}. - * - * @param separator the text that should appear between consecutive values in - * the resulting string (but not at the start or end) - * @param array an array of {@code byte} values, possibly empty - */ - public static String join(String separator, byte... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * (3 + separator.length())); - builder.append(toInt(array[0])); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(toString(array[i])); - } - return builder.toString(); - } - - /** - * Returns a comparator that compares two {@code byte} arrays - * lexicographically. That is, it compares, using {@link - * #compare(byte, byte)}), the first pair of values that follow any common - * prefix, or when one array is a prefix of the other, treats the shorter - * array as the lesser. For example, {@code [] < [0x01] < [0x01, 0x7F] < - * [0x01, 0x80] < [0x02]}. Values are treated as unsigned. - * - *

The returned comparator is inconsistent with {@link - * Object#equals(Object)} (since arrays support only identity equality), but - * it is consistent with {@link java.util.Arrays#equals(byte[], byte[])}. - * - * @see - * Lexicographical order article at Wikipedia - * @since 2.0 - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparatorHolder.BEST_COMPARATOR; - } - - static Comparator lexicographicalComparatorJavaImpl() { - return LexicographicalComparatorHolder.PureJavaComparator.INSTANCE; - } - - /** - * Provides a lexicographical comparator implementation; either a Java - * implementation or a faster implementation based on {@link Unsafe}. - * - *

Uses reflection to gracefully fall back to the Java implementation if - * {@code Unsafe} isn't available. - */ - static class LexicographicalComparatorHolder { - static final String UNSAFE_COMPARATOR_NAME = - LexicographicalComparatorHolder.class.getName() + "$UnsafeComparator"; - - static final Comparator BEST_COMPARATOR = getBestComparator(); - - enum UnsafeComparator implements Comparator { - INSTANCE; - - static final boolean BIG_ENDIAN = - ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN); - - /* - * The following static final fields exist for performance reasons. - * - * In UnsignedBytesBenchmark, accessing the following objects via static - * final fields is the fastest (more than twice as fast as the Java - * implementation, vs ~1.5x with non-final static fields, on x86_32) - * under the Hotspot server compiler. The reason is obviously that the - * non-final fields need to be reloaded inside the loop. - * - * And, no, defining (final or not) local variables out of the loop still - * isn't as good because the null check on the theUnsafe object remains - * inside the loop and BYTE_ARRAY_BASE_OFFSET doesn't get - * constant-folded. - * - * The compiler can treat static final fields as compile-time constants - * and can constant-fold them while (final or not) local variables are - * run time values. - */ - - static final Unsafe theUnsafe; - - /** The offset to the first element in a byte array. */ - static final int BYTE_ARRAY_BASE_OFFSET; - - static { - theUnsafe = getUnsafe(); - - BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); - - // sanity check - this should never fail - if (theUnsafe.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. - * Replace with a simple call to Unsafe.getUnsafe when integrating - * into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static Unsafe getUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) {} - try { - return java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction() { - public Unsafe run() throws Exception { - Class k = Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) - return k.cast(x); - } - throw new NoSuchFieldError("the Unsafe"); - }}); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", - e.getCause()); - } - } - - @Override public int compare(byte[] left, byte[] right) { - int minLength = Math.min(left.length, right.length); - int minWords = minLength / Longs.BYTES; - - /* - * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a - * time is no slower than comparing 4 bytes at a time even on 32-bit. - * On the other hand, it is substantially faster on 64-bit. - */ - for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) { - long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); - long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); - if (lw != rw) { - if (BIG_ENDIAN) { - return UnsignedLongs.compare(lw, rw); - } - - /* - * We want to compare only the first index where left[index] != right[index]. - * This corresponds to the least significant nonzero byte in lw ^ rw, since lw - * and rw are little-endian. Long.numberOfTrailingZeros(diff) tells us the least - * significant nonzero bit, and zeroing out the first three bits of L.nTZ gives us the - * shift to get that least significant nonzero byte. - */ - int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; - return (int) (((lw >>> n) & UNSIGNED_MASK) - ((rw >>> n) & UNSIGNED_MASK)); - } - } - - // The epilogue to cover the last (minLength % 8) elements. - for (int i = minWords * Longs.BYTES; i < minLength; i++) { - int result = UnsignedBytes.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - enum PureJavaComparator implements Comparator { - INSTANCE; - - @Override public int compare(byte[] left, byte[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - int result = UnsignedBytes.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } - - /** - * Returns the Unsafe-using Comparator, or falls back to the pure-Java - * implementation if unable to do so. - */ - static Comparator getBestComparator() { - try { - Class theClass = Class.forName(UNSAFE_COMPARATOR_NAME); - - // yes, UnsafeComparator does implement Comparator - @SuppressWarnings("unchecked") - Comparator comparator = - (Comparator) theClass.getEnumConstants()[0]; - return comparator; - } catch (Throwable t) { // ensure we really catch *everything* - return lexicographicalComparatorJavaImpl(); - } - } - } -} diff --git a/libtest/src/main/java/z/util/primitives/UnsignedInts.java b/libtest/src/main/java/z/util/primitives/UnsignedInts.java deleted file mode 100644 index a10382a51..000000000 --- a/libtest/src/main/java/z/util/primitives/UnsignedInts.java +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Comparator; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code int} primitives that interpret values as - * unsigned (that is, any negative value {@code x} is treated as the positive value - * {@code 2^32 + x}). The methods for which signedness is not an issue are in {@link Ints}, as well - * as signed versions of methods for which signedness is an issue. - * - *

In addition, this class provides several static methods for converting an {@code int} to a - * {@code String} and a {@code String} to an {@code int} that treat the {@code int} as an unsigned - * number. - * - *

Users of these utilities must be extremely careful not to mix up signed and unsigned - * {@code int} values. - * - *

See the Guava User Guide article on - * unsigned primitive utilities. - * - * @author Louis Wasserman - * @since 11.0 - */ -public final class UnsignedInts { - static final long INT_MASK = 0xffffffffL; - - private UnsignedInts() {} - - static int flip(int value) { - return value ^ Integer.MIN_VALUE; - } - - /** - * Compares the two specified {@code int} values, treating them as unsigned values between - * {@code 0} and {@code 2^32 - 1} inclusive. - * - * @param a the first unsigned {@code int} to compare - * @param b the second unsigned {@code int} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is - * greater than {@code b}; or zero if they are equal - */ - public static int compare(int a, int b) { - return Ints.compare(flip(a), flip(b)); - } - - /** - * Returns the value of the given {@code int} as a {@code long}, when treated as unsigned. - */ - public static long toLong(int value) { - return value & INT_MASK; - } - - /** - * Returns the least value present in {@code array}, treating values as unsigned. - * - * @param array a nonempty array of unsigned {@code int} values - * @return the value present in {@code array} that is less than or equal to every other value in - * the array according to {@link #compare} - * @throws IllegalArgumentException if {@code array} is empty - */ - public static int min(int... array) { - int min = flip(array[0]); - for (int i = 1; i < array.length; i++) { - int next = flip(array[i]); - if (next < min) { - min = next; - } - } - return flip(min); - } - - /** - * Returns the greatest value present in {@code array}, treating values as unsigned. - * - * @param array a nonempty array of unsigned {@code int} values - * @return the value present in {@code array} that is greater than or equal to every other value - * in the array according to {@link #compare} - * @throws IllegalArgumentException if {@code array} is empty - */ - public static int max(int... array) { - int max = flip(array[0]); - for (int i = 1; i < array.length; i++) { - int next = flip(array[i]); - if (next > max) { - max = next; - } - } - return flip(max); - } - - /** - * Returns a string containing the supplied unsigned {@code int} values separated by - * {@code separator}. For example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in the resulting - * string (but not at the start or end) - * @param array an array of unsigned {@code int} values, possibly empty - */ - public static String join(String separator, int... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * 5); - builder.append(toString(array[0])); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(toString(array[i])); - } - return builder.toString(); - } - - /** - * Returns a comparator that compares two arrays of unsigned {@code int} values lexicographically. - * That is, it compares, using {@link #compare(int, int)}), the first pair of values that follow - * any common prefix, or when one array is a prefix of the other, treats the shorter array as the - * lesser. For example, {@code [] < [1] < [1, 2] < [2] < [1 << 31]}. - * - *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays - * support only identity equality), but it is consistent with {@link java.util.Arrays#equals(int[], int[])}. - * - * @see Lexicographical order - * article at Wikipedia - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(int[] left, int[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - if (left[i] != right[i]) { - return UnsignedInts.compare(left[i], right[i]); - } - } - return left.length - right.length; - } - } - - /** - * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 32-bit - * quantities. - * - * @param dividend the dividend (numerator) - * @param divisor the divisor (denominator) - * @throws ArithmeticException if divisor is 0 - */ - public static int divide(int dividend, int divisor) { - return (int) (toLong(dividend) / toLong(divisor)); - } - - /** - * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 32-bit - * quantities. - * - * @param dividend the dividend (numerator) - * @param divisor the divisor (denominator) - * @throws ArithmeticException if divisor is 0 - */ - public static int remainder(int dividend, int divisor) { - return (int) (toLong(dividend) % toLong(divisor)); - } - - /** - * Returns the unsigned {@code int} value represented by the given decimal string. - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Integer#parseInt(String)}) - */ - public static int parseUnsignedInt(String s) { - return parseUnsignedInt(s, 10); - } - - /** - * Returns the unsigned {@code int} value represented by a string with the given radix. - * - * @param string the string containing the unsigned integer representation to be parsed. - * @param radix the radix to use while parsing {@code s}; must be between - * {@link Character#MIN_RADIX} and {@link Character#MAX_RADIX}. - * @throws NumberFormatException if the string does not contain a valid unsigned {@code int}, or - * if supplied radix is invalid. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Integer#parseInt(String)}) - */ - public static int parseUnsignedInt(String string, int radix) { - long result = Long.parseLong(string, radix); - if ((result & INT_MASK) != result) { - throw new NumberFormatException("Input " + string + " in base " + radix - + " is not in the range of an unsigned integer"); - } - return (int) result; - } - - /** - * Returns a string representation of x, where x is treated as unsigned. - */ - public static String toString(int x) { - return toString(x, 10); - } - - /** - * Returns a string representation of {@code x} for the given radix, where {@code x} is treated - * as unsigned. - * - * @param x the value to convert to a string. - * @param radix the radix to use while working with {@code x} - * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - */ - public static String toString(int x, int radix) { - long asLong = x & INT_MASK; - return Long.toString(asLong, radix); - } -} diff --git a/libtest/src/main/java/z/util/primitives/UnsignedLongs.java b/libtest/src/main/java/z/util/primitives/UnsignedLongs.java deleted file mode 100644 index 11bb48f86..000000000 --- a/libtest/src/main/java/z/util/primitives/UnsignedLongs.java +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright 2013, Landz and its contributors. 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 z.util.primitives; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.math.BigInteger; -import java.util.Comparator; - -/** - * modified by: - * @auther Landz's contributors - */ - -/** - * Static utility methods pertaining to {@code long} primitives that interpret values as - * unsigned (that is, any negative value {@code x} is treated as the positive value - * {@code 2^64 + x}). The methods for which signedness is not an issue are in {@link Longs}, as - * well as signed versions of methods for which signedness is an issue. - * - *

In addition, this class provides several static methods for converting a {@code long} to a - * {@code String} and a {@code String} to a {@code long} that treat the {@code long} as an unsigned - * number. - * - *

Users of these utilities must be extremely careful not to mix up signed and unsigned - * {@code long} values. - * - *

See the Guava User Guide article on - * unsigned primitive utilities. - * - * @author Louis Wasserman - * @author Brian Milch - * @author Colin Evans - * @since 10.0 - */ -public final class UnsignedLongs { - private UnsignedLongs() {} - - public static final long MAX_VALUE = -1L; // Equivalent to 2^64 - 1 - - /** - * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on - * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} - * as signed longs. - */ - private static long flip(long a) { - return a ^ Long.MIN_VALUE; - } - - /** - * Compares the two specified {@code long} values, treating them as unsigned values between - * {@code 0} and {@code 2^64 - 1} inclusive. - * - * @param a the first unsigned {@code long} to compare - * @param b the second unsigned {@code long} to compare - * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is - * greater than {@code b}; or zero if they are equal - */ - public static int compare(long a, long b) { - return Longs.compare(flip(a), flip(b)); - } - - /** - * Returns the least value present in {@code array}, treating values as unsigned. - * - * @param array a nonempty array of unsigned {@code long} values - * @return the value present in {@code array} that is less than or equal to every other value in - * the array according to {@link #compare} - * @throws IllegalArgumentException if {@code array} is empty - */ - public static long min(long... array) { - long min = flip(array[0]); - for (int i = 1; i < array.length; i++) { - long next = flip(array[i]); - if (next < min) { - min = next; - } - } - return flip(min); - } - - /** - * Returns the greatest value present in {@code array}, treating values as unsigned. - * - * @param array a nonempty array of unsigned {@code long} values - * @return the value present in {@code array} that is greater than or equal to every other value - * in the array according to {@link #compare} - * @throws IllegalArgumentException if {@code array} is empty - */ - public static long max(long... array) { - long max = flip(array[0]); - for (int i = 1; i < array.length; i++) { - long next = flip(array[i]); - if (next > max) { - max = next; - } - } - return flip(max); - } - - /** - * Returns a string containing the supplied unsigned {@code long} values separated by - * {@code separator}. For example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}. - * - * @param separator the text that should appear between consecutive values in the resulting - * string (but not at the start or end) - * @param array an array of unsigned {@code long} values, possibly empty - */ - public static String join(String separator, long... array) { - if (array.length == 0) { - return ""; - } - - // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * 5); - builder.append(toString(array[0])); - for (int i = 1; i < array.length; i++) { - builder.append(separator).append(toString(array[i])); - } - return builder.toString(); - } - - /** - * Returns a comparator that compares two arrays of unsigned {@code long} values - * lexicographically. That is, it compares, using {@link #compare(long, long)}), the first pair of - * values that follow any common prefix, or when one array is a prefix of the other, treats the - * shorter array as the lesser. For example, {@code [] < [1L] < [1L, 2L] < [2L] < [1L << 63]}. - * - *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays - * support only identity equality), but it is consistent with - * {@link java.util.Arrays#equals(long[], long[])}. - * - * @see Lexicographical order - * article at Wikipedia - */ - public static Comparator lexicographicalComparator() { - return LexicographicalComparator.INSTANCE; - } - - enum LexicographicalComparator implements Comparator { - INSTANCE; - - @Override - public int compare(long[] left, long[] right) { - int minLength = Math.min(left.length, right.length); - for (int i = 0; i < minLength; i++) { - if (left[i] != right[i]) { - return UnsignedLongs.compare(left[i], right[i]); - } - } - return left.length - right.length; - } - } - - /** - * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit - * quantities. - * - * @param dividend the dividend (numerator) - * @param divisor the divisor (denominator) - * @throws ArithmeticException if divisor is 0 - */ - public static long divide(long dividend, long divisor) { - if (divisor < 0) { // i.e., divisor >= 2^63: - if (compare(dividend, divisor) < 0) { - return 0; // dividend < divisor - } else { - return 1; // dividend >= divisor - } - } - - // Optimization - use signed division if dividend < 2^63 - if (dividend >= 0) { - return dividend / divisor; - } - - /* - * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is - * guaranteed to be either exact or one less than the correct value. This follows from fact - * that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not - * quite trivial. - */ - long quotient = ((dividend >>> 1) / divisor) << 1; - long rem = dividend - quotient * divisor; - return quotient + (compare(rem, divisor) >= 0 ? 1 : 0); - } - - /** - * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 64-bit - * quantities. - * - * @param dividend the dividend (numerator) - * @param divisor the divisor (denominator) - * @throws ArithmeticException if divisor is 0 - * @since 11.0 - */ - public static long remainder(long dividend, long divisor) { - if (divisor < 0) { // i.e., divisor >= 2^63: - if (compare(dividend, divisor) < 0) { - return dividend; // dividend < divisor - } else { - return dividend - divisor; // dividend >= divisor - } - } - - // Optimization - use signed modulus if dividend < 2^63 - if (dividend >= 0) { - return dividend % divisor; - } - - /* - * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is - * guaranteed to be either exact or one less than the correct value. This follows from fact - * that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not - * quite trivial. - */ - long quotient = ((dividend >>> 1) / divisor) << 1; - long rem = dividend - quotient * divisor; - return rem - (compare(rem, divisor) >= 0 ? divisor : 0); - } - - /** - * Returns the unsigned {@code long} value represented by the given decimal string. - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} - * value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Long#parseLong(String)}) - */ - public static long parseUnsignedLong(String s) { - return parseUnsignedLong(s, 10); - } - - /** - * Returns the unsigned {@code long} value represented by a string with the given radix. - * - * @param s the string containing the unsigned {@code long} representation to be parsed. - * @param radix the radix to use while parsing {@code s} - * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} - * with the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Long#parseLong(String)}) - */ - public static long parseUnsignedLong(String s, int radix) { - if (s.length() == 0) { - throw new NumberFormatException("empty string"); - } - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new NumberFormatException("illegal radix: " + radix); - } - - int max_safe_pos = maxSafeDigits[radix] - 1; - long value = 0; - for (int pos = 0; pos < s.length(); pos++) { - int digit = Character.digit(s.charAt(pos), radix); - if (digit == -1) { - throw new NumberFormatException(s); - } - if (pos > max_safe_pos && overflowInParse(value, digit, radix)) { - throw new NumberFormatException("Too large for unsigned long: " + s); - } - value = (value * radix) + digit; - } - - return value; - } - - /** - * Returns true if (current * radix) + digit is a number too large to be represented by an - * unsigned long. This is useful for detecting overflow while parsing a string representation of - * a number. Does not verify whether supplied radix is valid, passing an invalid radix will give - * undefined results or an ArrayIndexOutOfBoundsException. - */ - private static boolean overflowInParse(long current, int digit, int radix) { - if (current >= 0) { - if (current < maxValueDivs[radix]) { - return false; - } - if (current > maxValueDivs[radix]) { - return true; - } - // current == maxValueDivs[radix] - return (digit > maxValueMods[radix]); - } - - // current < 0: high bit is set - return true; - } - - /** - * Returns a string representation of x, where x is treated as unsigned. - */ - public static String toString(long x) { - return toString(x, 10); - } - - /** - * Returns a string representation of {@code x} for the given radix, where {@code x} is treated - * as unsigned. - * - * @param x the value to convert to a string. - * @param radix the radix to use while working with {@code x} - * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - */ - public static String toString(long x, int radix) { - if (x == 0) { - // Simply return "0" - return "0"; - } else { - char[] buf = new char[64]; - int i = buf.length; - if (x < 0) { - // Separate off the last digit using unsigned division. That will leave - // a number that is nonnegative as a signed integer. - long quotient = divide(x, radix); - long rem = x - quotient * radix; - buf[--i] = Character.forDigit((int) rem, radix); - x = quotient; - } - // Simple modulo/division approach - while (x > 0) { - buf[--i] = Character.forDigit((int) (x % radix), radix); - x /= radix; - } - // Generate string - return new String(buf, i, buf.length - i); - } - } - - // calculated as 0xffffffffffffffff / radix - private static final long[] maxValueDivs = new long[Character.MAX_RADIX + 1]; - private static final int[] maxValueMods = new int[Character.MAX_RADIX + 1]; - private static final int[] maxSafeDigits = new int[Character.MAX_RADIX + 1]; - static { - BigInteger overflow = new BigInteger("10000000000000000", 16); - for (int i = Character.MIN_RADIX; i <= Character.MAX_RADIX; i++) { - maxValueDivs[i] = divide(MAX_VALUE, i); - maxValueMods[i] = (int) remainder(MAX_VALUE, i); - maxSafeDigits[i] = overflow.toString(i).length() - 1; - } - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDeque.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDeque.java deleted file mode 100644 index 38eac5f3a..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDeque.java +++ /dev/null @@ -1,257 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicMarkableReference; - -// Double-linked list by Sundell -public class LockFreeDeque { - - private final Node head; - private final Node tail; - - public LockFreeDeque() { - this.head = new Node(null, null, null); - this.tail = new Node(null, null, null); - - head.next.compareAndSet(null, tail, false, false); - tail.prev.compareAndSet(null, head, false, false); - } - - private class Node { - AtomicMarkableReference prev, next; - T val; - - Node(T val, Node prev, Node next) { - this.val = val; - this.prev = new AtomicMarkableReference<>(prev, false); - this.next = new AtomicMarkableReference<>(next, false); - } - } - - public void pushRight(T x) { - Node node = new Node(x, null, null); - Node next = tail; - Node prev = next.prev.getReference(); - while (true) { - if (!prev.next.compareAndSet(next, next, false, false)) { - // concurrent push inserted -> get new prev - prev = helpInsert(prev, next, "concurrentPushRight"); - continue; - } - // 0 push step - node.prev = new AtomicMarkableReference<>(prev, false); - node.next = new AtomicMarkableReference<>(next, false); - // 1 push step - if (prev.next.compareAndSet(next, node, false, false)) { - break; - } - } - // 2 push step - pushCommon(node, next); - } - - public void pushLeft(T x) { - Node node = new Node(x, null, null); - Node prev = head; - Node next = prev.next.getReference(); - while (true) { - if (!prev.next.compareAndSet(next, next, false, false)) { - next = prev.next.getReference(); - continue; - } - node.prev = new AtomicMarkableReference<>(prev, false); - node.next = new AtomicMarkableReference<>(next, false); - - if (prev.next.compareAndSet(next, node, false, false)) { - break; - } - } - pushCommon(node, next); - } - - private void pushCommon(Node node, Node next) { - while (true) { - AtomicMarkableReference link1 = next.prev; - if (link1.isMarked() || !node.next.compareAndSet(next, next, false, false)) { - break; - } - if (next.prev.compareAndSet(link1.getReference(), node, false, false)) { - if (node.prev.isMarked()) { - helpInsert(node, next, "pushCommon"); - } - break; - } - } - } - - public T popLeft() { - T value; - Node prev = head; - while (true) { - Node node = prev.next.getReference(); - // deque is empty - if (node == tail) { - return null; - } - boolean[] removed = new boolean[1]; - Node nodeNext = node.next.get(removed); - // concurrent pop started to delete this node, help it, then continue - if (removed[0]) { - helpDelete(node, "help concurrent"); - continue; - } - // 1 pop step - if (node.next.compareAndSet(nodeNext, nodeNext, false, true)) { - // 2, 3 step - helpDelete(node, "1st step"); - Node next = node.next.getReference(); - // 4 step - helpInsert(prev, next, "popLeft New"); - value = node.val; - return value; - } - } - } - - public T popRight() { - Node next = tail; - Node node = next.prev.getReference(); - while (true) { - if (!node.next.compareAndSet(next, next, false, false)) { - node = helpInsert(node, next, "popRight"); - continue; - } - if (node == head) { - return null; - } - if (node.next.compareAndSet(next, next, false, true)) { - helpDelete(node, ""); - Node prev = node.prev.getReference(); - helpInsert(prev, next, "popRight"); - return node.val; - } - } - } - - /** - * Correct node.prev to the closest previous node - * helpInsert is very weak - does not reset node.prev to the actual prev.next - * but just tries to set node.prev to the given suggestion of a prev node - * (for 2 push step, 4 pop step) - */ - private Node helpInsert(Node prev, Node node, String method) { - // last = is the last node : last.next == prev and it is not marked as removed - Node last = null; - while (true) { - - boolean[] removed = new boolean[1]; - Node prevNext = prev.next.get(removed); - - if (removed[0]) { - if (last != null) { - markPrev(prev); - Node next2 = prev.next.getReference(); - boolean b1 = last.next.compareAndSet(prev, next2, false, false); - prev = last; - last = null; - } else { - prevNext = prev.prev.getReference(); - prev = prevNext; - } - continue; - } - - Node nodePrev = node.prev.get(removed); - if (removed[0]) { - break; - } - - // prev is not the previous node of node - if (prevNext != node) { - last = prev; - prev = prevNext; - continue; - } - - if (nodePrev == prev) break; - if (prev.next.getReference() == node && node.prev.compareAndSet(nodePrev, prev, false, false)) { - if (prev.prev.isMarked()) { - continue; - } - break; - } - } - return prev; - } - - // 2 and 3 pop steps - private void helpDelete(Node node, String place) { - markPrev(node); - Node prev = node.prev.getReference(); - Node next = node.next.getReference(); - Node last = null; - while (true) { - if (prev == next) break; - if (next.next.isMarked()) { - markPrev(next); - next = next.next.getReference(); - continue; - } - boolean removed[] = new boolean[1]; - Node prevNext = prev.next.get(removed); - if (removed[0]) { - if (last != null) { - markPrev(prev); - Node next2 = prev.next.getReference(); - last.next.compareAndSet(prev, next2, false, false); - prev = last; - last = null; - } else { - prevNext = prev.prev.getReference(); - prev = prevNext; - assert (prev != null); - } - continue; - } - if (prevNext != node) { - last = prev; - prev = prevNext; - continue; - } - if (prev.next.compareAndSet(node, next, false, false)) { - break; - } - } - } - - private void markPrev(Node node) { - while (true) { - AtomicMarkableReference link1 = node.prev; - if (link1.isMarked() || node.prev.compareAndSet(link1.getReference(), link1.getReference(), false, true)) { - break; - } - } - } - -} \ No newline at end of file diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDequeTest.kt b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDequeTest.kt deleted file mode 100644 index 01b05b624..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/LockFreeDequeTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests - -import org.jetbrains.kotlinx.lincheck.LinChecker -import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchCTest -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest -import org.junit.Test - -@StressCTest(requireStateEquivalenceImplCheck = false) -class DequeLinearizabilityTest { - private val deque = LockFreeDeque(); - -/*- - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - - @Operation - fun pushLeft(value: Int) = deque.pushLeft(value) - - @Operation - fun pushRight(value: Int) = deque.pushRight(value) - - @Operation - fun popLeft(): Int? = deque.popLeft() - - @Operation - fun popRight(): Int? = deque.popRight() - - @Test - fun test() = LinChecker.check(DequeLinearizabilityTest::class.java) -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/BitVectorCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/BitVectorCorrect1.java deleted file mode 100644 index c473f19c9..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/BitVectorCorrect1.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.boundary; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.cliffc.high_scale_lib.NonBlockingSetInt; -import org.junit.Test; - -import java.util.Set; - -@StressCTest -@Param(name = "key", gen = IntGen.class, conf = "1:10") -public class BitVectorCorrect1 { - private Set q = new NonBlockingSetInt(); - - @Operation(params = {"key"}) - public boolean add(int key) { - return q.add(key); - } - - @Operation - public boolean remove(@Param(name = "key") int key) { - return q.remove(key); - } - - @Test - public void test() { - LinChecker.check(BitVectorCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/MapCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/MapCorrect1.java deleted file mode 100644 index 9ce42e2f3..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/MapCorrect1.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.boundary; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.cliffc.high_scale_lib.NonBlockingHashMap; -import org.junit.Test; - -import java.util.Map; - -@StressCTest -@Param(name = "key", gen = IntGen.class) -@Param(name = "value", gen = IntGen.class) -public class MapCorrect1 { - private Map map = new NonBlockingHashMap<>(); - - @Operation - public Integer put(Integer key, Integer value) { - return map.put(key, value); - } - - @Operation - public Integer get(Integer key) { - return map.get(key); - } - - @Operation(handleExceptionsAsResult = NullPointerException.class) - public int putIfAbsent(int key, int value) { - return map.putIfAbsent(key, value); - } - - @Test - public void test() { - LinChecker.check(MapCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/SetCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/SetCorrect1.java deleted file mode 100644 index 8cc09c42c..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/boundary/SetCorrect1.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.boundary; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.cliffc.high_scale_lib.NonBlockingHashSet; -import org.junit.Test; - -@StressCTest -@Param(name = "key", gen = IntGen.class) -public class SetCorrect1 { - private NonBlockingHashSet q = new NonBlockingHashSet<>(); - - @Operation(params = {"key"}) - public boolean add(int key) { - return q.add(key); - } - - @Operation(params = {"key"}) - public boolean remove(int key) { - return q.remove(key); - } - - @Test - public void test() { - LinChecker.check(SetCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterGetTest.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterGetTest.java deleted file mode 100644 index e27e5d65d..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterGetTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState; -import org.junit.Test; -import tests.custom.counter.CounterGet; - -@StressCTest -public class CounterGetTest extends VerifierState { - private CounterGet counter = new CounterGet();; - - @Operation - public int incAndGet() { - return counter.incrementAndGet(); - } - - @Operation - public int get() { - return counter.get(); - } - - @Test - public void test() { - LinChecker.check(CounterGetTest.class); - } - - @Override - protected Object extractState() { - return counter.get(); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest1.java deleted file mode 100644 index 2dc567c2d..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest1.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.counter.Counter; -import tests.custom.counter.CounterWrong0; - -@StressCTest -public class CounterTest1 { - private Counter counter = new CounterWrong0(); - - @Operation - public int incAndGet() { - return counter.incrementAndGet(); - } - - @Test(expected = AssertionError.class) - public void test() { - LinChecker.check(CounterTest1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest2.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest2.java deleted file mode 100644 index dba25915f..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest2.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.counter.Counter; -import tests.custom.counter.CounterCorrect2; - -@StressCTest -public class CounterTest2 { - private Counter counter = new CounterCorrect2(); - - @Operation - public int incAndGet() { - return counter.incrementAndGet(); - } - - @Test - public void test() { - LinChecker.check(CounterTest2.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest3.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest3.java deleted file mode 100644 index f78d9b9ab..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest3.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.counter.Counter; -import tests.custom.counter.CounterWrong1; - -@StressCTest -public class CounterTest3 { - private Counter counter = new CounterWrong1(); - - @Operation - public int incAndGet() { - return counter.incrementAndGet(); - } - - @Test(expected = AssertionError.class) - public void test() { - LinChecker.check(CounterTest3.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest4.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest4.java deleted file mode 100644 index a243991fe..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/counter/CounterTest4.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.counter; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.counter.Counter; -import tests.custom.counter.CounterWrong2; - -@StressCTest -public class CounterTest4 { - private Counter counter = new CounterWrong2(); - - @Operation - public int incAndGet() { - return counter.incrementAndGet(); - } - - @Test(expected = AssertionError.class) - public void test() { - LinChecker.check(CounterTest4.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueCorrect1.java deleted file mode 100644 index a432af23e..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueCorrect1.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.queue.Queue; -import tests.custom.queue.QueueEmptyException; -import tests.custom.queue.QueueSynchronized; - -@StressCTest -public class WrapperQueueCorrect1 { - private Queue queue = new QueueSynchronized(10);; - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public void put(@Param(gen = IntGen.class)int args) throws Exception { - queue.put(args); - } - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public int get() throws Exception { - return queue.get(); - } - - @Test - public void test() { - LinChecker.check(WrapperQueueCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong1.java deleted file mode 100644 index 7d019fbb5..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong1.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.queue.Queue; -import tests.custom.queue.QueueEmptyException; -import tests.custom.queue.QueueWrong1; -@StressCTest -public class WrapperQueueWrong1 { - private Queue queue = new QueueWrong1(10); - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public void put(@Param(gen = IntGen.class)int x) throws Exception { - queue.put(x); - } - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public int get() throws Exception { - return queue.get(); - } - - @Test(expected = AssertionError.class) - public void test() { - LinChecker.check(WrapperQueueWrong1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong2.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong2.java deleted file mode 100644 index 551d2e3f0..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong2.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.queue.Queue; -import tests.custom.queue.QueueEmptyException; -import tests.custom.queue.QueueWrong2; - -@StressCTest -public class WrapperQueueWrong2 { - private Queue queue = new QueueWrong2(10); - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public void put(@Param(gen = IntGen.class)int args) throws Exception { - queue.put(args); - } - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public int get() throws Exception { - return queue.get(); - } - - @Test(expected = AssertionError.class) - public void test() throws Exception { - LinChecker.check(WrapperQueueWrong2.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong3.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong3.java deleted file mode 100644 index d73d61fdb..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/queue/WrapperQueueWrong3.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import tests.custom.queue.Queue; -import tests.custom.queue.QueueEmptyException; -import tests.custom.queue.QueueWrong3; - -@StressCTest(requireStateEquivalenceImplCheck = false) -public class WrapperQueueWrong3 { - private Queue queue = new QueueWrong3(10); - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public void put(@Param(gen = IntGen.class)int args) throws Exception { - queue.put(args); - } - - @Operation(handleExceptionsAsResult = QueueEmptyException.class) - public int get() throws Exception { - return queue.get(); - } - - @Test(expected = AssertionError.class) - public void test() throws Exception { - LinChecker.check(WrapperQueueWrong2.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/transfer/AccountsTest.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/transfer/AccountsTest.java deleted file mode 100644 index 57eed01d0..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/custom/transfer/AccountsTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.custom.transfer; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.annotations.*; -import org.jetbrains.kotlinx.lincheck.*; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.*; -import org.jetbrains.kotlinx.lincheck.paramgen.*; -import org.jetbrains.kotlinx.lincheck.strategy.stress.*; -import org.jetbrains.kotlinx.lincheck.verifier.*; -import org.junit.*; -import org.junit.runner.*; -import org.junit.runners.*; -import tests.custom.transfer.*; - -import java.util.*; -import java.util.function.*; - -@RunWith(Parameterized.class) -public class AccountsTest { - private static Supplier accountCreator; - - @Parameterized.Parameters(name = "{1}") - public static List params() { - return Arrays.asList( - new Object[] {(Supplier) AccountsWrong1::new, "AccountsWrong1"}, - new Object[] {(Supplier) AccountsWrong2::new, "AccountsWrong2"}, - new Object[] {(Supplier) AccountsWrong3::new, "AccountsWrong3"}, - new Object[] {(Supplier) AccountsWrong4::new, "AccountsWrong4"} - ); - } - - public AccountsTest(Supplier accountCreator, String desc) { - AccountsTest.accountCreator = accountCreator; - } - - @StressCTest(threads = 3, actorsPerThread = 3) - @Param(name = "id", gen = IntGen.class, conf = "1:4") - @Param(name = "amount", gen = IntGen.class) - public static class AccountsLinearizabilityTest extends VerifierState { - private final Accounts acc = accountCreator.get(); - - @Operation(params = {"id"}) - public int getAmount(int key) { - return acc.getAmount(key); - } - - @Operation(params = {"id", "amount"}) - public void setAmount(int key, int value) { - acc.setAmount(key, value); - } - - @Operation - public void transfer(@Param(name = "id") int from, @Param(name = "id") int to, - @Param(name = "amount") int amount) - { - acc.transfer(from, to, amount); - } - - @NotNull - @Override - protected Object extractState() { - List amounts = new ArrayList<>(); - for (int id = 1; id <= 4; id++) - amounts.add(getAmount(id)); - return amounts; - } - } - - @Test(expected = AssertionError.class) - public void test() { - LinChecker.check(AccountsLinearizabilityTest.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/MultisetCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/MultisetCorrect1.java deleted file mode 100644 index 7a80a828c..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/MultisetCorrect1.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.guava; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import com.google.common.collect.ConcurrentHashMultiset; -import org.junit.Test; - -@StressCTest -@Param(name = "value", gen = IntGen.class) -@Param(name = "count", gen = IntGen.class, conf = "1:10") -public class MultisetCorrect1 { - private ConcurrentHashMultiset q = ConcurrentHashMultiset.create(); - - @Operation(params = {"value", "count"}) - public int add(int value, int count) { - return q.add(value, count); - } - - @Operation(params = {"value", "count"}) - public int remove(int value, int count) { - return q.remove(value, count); - } - - @Test - public void test() { - LinChecker.check(MultisetCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/SetCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/SetCorrect1.java deleted file mode 100644 index e331c0866..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/guava/SetCorrect1.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.guava; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; - -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -@StressCTest -@Param(name = "key", gen = IntGen.class) -public class SetCorrect1 { - private Set q = Collections.newSetFromMap(new ConcurrentHashMap());; - - @Operation(params = {"key"}) - public boolean add(Integer params) { - return q.add(params); - } - - @Operation(params = {"key"}) - public boolean remove(Integer params) { - return q.remove(params); - } - - @Test - public void test() throws Exception { - LinChecker.check(SetCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/blocking_queue/BlockingQueueTest1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/blocking_queue/BlockingQueueTest1.java deleted file mode 100644 index bc80144d7..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/blocking_queue/BlockingQueueTest1.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.juc.blocking_queue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; - -import java.util.NoSuchElementException; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - -@StressCTest -public class BlockingQueueTest1 { - private BlockingQueue q = new ArrayBlockingQueue<>(10); - - @Operation - public boolean add(@Param(gen = IntGen.class) Integer value) { - return q.add(value); - } - - @Operation(handleExceptionsAsResult = NoSuchElementException.class) - public Integer element() { - return q.element(); - } - - @Operation(handleExceptionsAsResult = NoSuchElementException.class) - public Integer remove() { - return q.remove(); - } - - @Operation(handleExceptionsAsResult = NoSuchElementException.class) - public Integer poll() { - return q.poll(); - } - - @Test - public void test() { - LinChecker.check(BlockingQueueTest1.class); - } -} - diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/hash_map/HashMapTest.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/hash_map/HashMapTest.java deleted file mode 100644 index 9e123cbe4..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/juc/hash_map/HashMapTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.juc.hash_map; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -@StressCTest -@Param(name = "key", gen = IntGen.class) -@Param(name = "value", gen = IntGen.class) -public class HashMapTest extends VerifierState { - private Map m = new HashMap<>(); - - @Operation(params = {"key", "value"}) - public Integer put(Integer key, Integer value) { - return m.put(key, value); - } - - @Operation - public Integer get(@Param(name = "key") Integer key) { - return m.get(key); - } - - @Test(expected = AssertionError.class) - public void test() throws Exception { - LinChecker.check(HashMapTest.class); - } - - @Override - protected Object extractState() { - return m; - } -} - diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/MutexStressTest.kt b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/MutexStressTest.kt deleted file mode 100644 index 05b3109e7..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/MutexStressTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/*- - * #%L - * libtest - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.tests.linearizability - -import kotlinx.coroutines.sync.Mutex -import org.jetbrains.kotlinx.lincheck.LinChecker -import org.jetbrains.kotlinx.lincheck.LoggingLevel -import org.jetbrains.kotlinx.lincheck.annotations.LogLevel -import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.annotations.Param -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.lang.IllegalStateException - -@Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(actorsPerThread = 10, threads = 2, invocationsPerIteration = 10000, iterations = 100, actorsBefore = 10, actorsAfter = 0, requireStateEquivalenceImplCheck = false) -class MutexStressTest : VerifierState() { - val mutex = Mutex(true) - - @Operation(handleExceptionsAsResult = [IllegalStateException::class]) - fun tryLock(e: Int) = mutex.tryLock(e) - - @Operation(handleExceptionsAsResult = [IllegalStateException::class]) - suspend fun lock(e: Int) = mutex.lock(e) - - @Operation - fun holdsLock(e: Int) = mutex.holdsLock(e) - - @Operation(handleExceptionsAsResult = [IllegalStateException::class]) - fun unlock(e: Int) = mutex.unlock(e) - - @Test - fun test() = LinChecker.check(MutexStressTest::class.java) - - override fun extractState() = mutex -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/lockfreequeue/QueueCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/lockfreequeue/QueueCorrect1.java deleted file mode 100644 index f66f40eaa..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/lockfreequeue/QueueCorrect1.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.lockfreequeue; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import com.github.lock.free.queue.LockFreeQueue; - -/** - * https://github.com/yaitskov/lock-free-queue - */ -@StressCTest -public class QueueCorrect1 { - private LockFreeQueue q = new LockFreeQueue<>(); - - @Operation - public void add(@Param(gen = IntGen.class) int value) { - q.add(value); - } - - @Operation - public Object takeOrNull() { - return q.takeOrNull(); - } - - // @Test TODO is it really correct? - public void test() { - LinChecker.check(QueueCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/romix/TrieCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/romix/TrieCorrect1.java deleted file mode 100644 index 8f3ee1475..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/romix/TrieCorrect1.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.romix; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.junit.Test; -import romix.scala.collection.concurrent.TrieMap; - -import java.util.Map; - -@StressCTest -@Param(name = "key", gen = IntGen.class) -@Param(name = "value", gen = IntGen.class) -public class TrieCorrect1 { - private Map m = new TrieMap<>(); - - @Operation(params = {"key", "value"}) - public Integer put(Integer key, Integer value) { - return m.put(key, value); - } - - @Operation(params = {"key"}) - public Integer get(Integer key) { - return m.get(key); - } - - @Test - public void test() { - LinChecker.check(TrieCorrect1.class); - } -} - diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect1.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect1.java deleted file mode 100644 index f481f76c7..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect1.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.zchannel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import z.channel.GenericMPMCQueue; - -/** - * http://landz.github.io/ - */ -@StressCTest -public class QueueCorrect1 { - private GenericMPMCQueue q = new GenericMPMCQueue<>(4); - - @Operation - public boolean offer(@Param(gen = IntGen.class) int value) { - return q.offer(value); - } - - @Operation - public Integer poll() { - return q.poll(); - } - -// @Test TODO is it really correct? - public void test() throws Exception { - LinChecker.check(QueueCorrect1.class); - } -} diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect2.java b/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect2.java deleted file mode 100644 index 4d31482ae..000000000 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/zchannel/QueueCorrect2.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.tests.zchannel; - -/* - * #%L - * libtest - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import z.channel.GenericMPMCQueue; - -/** - * http://landz.github.io/ - */ -@StressCTest -public class QueueCorrect2 { - private GenericMPMCQueue q = new GenericMPMCQueue<>(16); - - @Operation - public boolean offer(@Param(gen = IntGen.class) int value) { - return q.offer(value); - } - - @Operation - public Integer poll() { - return q.poll(); - } - - // @Test TODO is it really correct? - public void test() throws Exception { - LinChecker.check(QueueCorrect2.class); - } -} \ No newline at end of file diff --git a/lincheck/pom.xml b/lincheck/pom.xml deleted file mode 100755 index 5757627f1..000000000 --- a/lincheck/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - org.jetbrains.kotlinx - lincheck-all - 2.7-SNAPSHOT - - 4.0.0 - - lincheck - Lincheck - - - - org.ow2.asm - asm-commons - ${asm.version} - - - org.ow2.asm - asm-util - ${asm.version} - - - - org.jctools - jctools-core - 2.1.0 - test - - - org.jetbrains.kotlinx - kotlinx-coroutines-core - 1.3.1 - - - org.jetbrains.kotlin - kotlin-reflect - 1.3.40 - - - - - \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.java deleted file mode 100644 index 10a1dccd0..000000000 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.java +++ /dev/null @@ -1,195 +0,0 @@ -package org.jetbrains.kotlinx.lincheck; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.annotations.LogLevel; -import org.jetbrains.kotlinx.lincheck.execution.*; -import org.jetbrains.kotlinx.lincheck.strategy.Strategy; -import org.jetbrains.kotlinx.lincheck.verifier.*; -import static org.jetbrains.kotlinx.lincheck.ReporterKt.DEFAULT_LOG_LEVEL; -import java.util.*; - - -/** - * This class runs concurrent tests. - * See {@link #check(Class)} and {@link #check(Class, Options)} methods for details. - */ -public class LinChecker { - private final Class testClass; - private final List testConfigurations; - private final CTestStructure testStructure; - private final Reporter reporter; - - private LinChecker(Class testClass, Options options) { - this.testClass = testClass; - this.testStructure = CTestStructure.getFromTestClass(testClass); - LoggingLevel logLevel; - if (options != null) { - logLevel = options.logLevel; - this.testConfigurations = Collections.singletonList(options.createTestConfigurations(testClass)); - } else { - logLevel = getLogLevelFromAnnotation(); - this.testConfigurations = CTestConfiguration.createFromTestClass(testClass); - } - this.reporter = new Reporter(logLevel); - } - - /** - * Runs all concurrent tests described with {@code @CTest} annotations on the specified test class. - * - * @throws AssertionError if algorithm or data structure is not correct. - */ - public static void check(Class testClass) { - check(testClass, null); - } - - /** - * Runs concurrent test on specified class with the specified by options environment. - *

- * NOTE: this method ignores {@code @CTest} annotations on the test class. - * - * @throws AssertionError if algorithm or data structure is not correct. - */ - public static void check(Class testClass, Options options) { - new LinChecker(testClass, options).check(); - } - - /** - * @throws AssertionError if algorithm or data structure is not correct - */ - private void check() throws AssertionError { - if (testConfigurations.isEmpty()) { - throw new IllegalStateException("No Lin-Check test configuration to run"); - } - testConfigurations.forEach(testConfiguration -> { - try { - checkImpl(testConfiguration); - } catch (RuntimeException | AssertionError e) { - throw e; - } catch (Exception e) { - throw new IllegalStateException(e); - } - }); - } - - private void checkImpl(CTestConfiguration testCfg) throws AssertionError, Exception { - ExecutionGenerator exGen = createExecutionGenerator(testCfg.generatorClass, testCfg); - // Run iterations - for (int iteration = 1; iteration <= testCfg.iterations; iteration++) { - ExecutionScenario scenario = exGen.nextExecution(); - // Check correctness of the generated scenario - reporter.logIteration(iteration, testCfg.iterations, scenario); - try { - runScenario(scenario, testCfg); - } catch (AssertionError e) { - if (!testCfg.minimizeFailedScenario) throw e; - minimizeScenario(scenario, testCfg, e); - } - } - } - - // Tries to minimize the specified failing scenario to make the error easier to understand. - // The algorithm is greedy: it tries to remove one actor from the scenario and checks - // whether a test with the modified one fails with error as well. If it fails, - // then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. - // Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. - // Thus, the algorithm works in the linear time of the total number of actors. - private void minimizeScenario(ExecutionScenario scenario, CTestConfiguration testCfg, AssertionError currentError) throws AssertionError, Exception { - reporter.logScenarioMinimization(scenario); - for (int i = 0; i < scenario.parallelExecution.size(); i++) { - for (int j = 0; j < scenario.parallelExecution.get(i).size(); j++) { - ExecutionScenario newScenario = copyScenario(scenario); - newScenario.parallelExecution.get(i).remove(j); - if (newScenario.parallelExecution.get(i).isEmpty()) - newScenario.parallelExecution.remove(i); // remove empty thread - minimizeNewScenarioAttempt(newScenario, testCfg); - } - } - for (int i = 0; i < scenario.initExecution.size(); i++) { - ExecutionScenario newScenario = copyScenario(scenario); - newScenario.initExecution.remove(i); - minimizeNewScenarioAttempt(newScenario, testCfg); - } - for (int i = 0; i < scenario.postExecution.size(); i++) { - ExecutionScenario newScenario = copyScenario(scenario); - newScenario.postExecution.remove(i); - minimizeNewScenarioAttempt(newScenario, testCfg); - } - throw currentError; - } - - private void minimizeNewScenarioAttempt(ExecutionScenario newScenario, CTestConfiguration testCfg) throws AssertionError, Exception { - try { - runScenario(newScenario, testCfg); - } catch (IllegalArgumentException e) { - // Ignore incorrect scenarios - } catch (AssertionError e) { - // Scenario has been minimized! Try to minimize more! - minimizeScenario(newScenario, testCfg, e); - throw new IllegalStateException("Never reaches, the previous `minimizeScenario` call always throw an exception"); - } - } - - private ExecutionScenario copyScenario(ExecutionScenario scenario) { - List initExecution = new ArrayList<>(scenario.initExecution); - List> parallelExecution = new ArrayList<>(); - for (int i = 0; i < scenario.parallelExecution.size(); i++) { - parallelExecution.add(new ArrayList<>(scenario.parallelExecution.get(i))); - } - List postExecution = new ArrayList<>(scenario.postExecution); - return new ExecutionScenario(initExecution, parallelExecution, postExecution); - } - - private void runScenario(ExecutionScenario scenario, CTestConfiguration testCfg) throws AssertionError, Exception { - validateScenario(testCfg, scenario); - Verifier verifier = createVerifier(testCfg.verifierClass, testCfg.sequentialSpecification); - if (testCfg.requireStateEquivalenceImplCheck) verifier.checkStateEquivalenceImplementation(); - Strategy strategy = Strategy.createStrategy(testCfg, testClass, scenario, verifier, reporter); - strategy.run(); - } - - private void validateScenario(CTestConfiguration testCfg, ExecutionScenario scenario) { - if (scenario.hasSuspendableActors()) { - if (scenario.initExecution.stream().anyMatch(Actor::isSuspendable)) - throw new IllegalArgumentException("Generated execution scenario for the test class with suspendable methods contains suspendable actors in initial part"); - if (scenario.parallelExecution.stream().anyMatch(actors -> actors.stream().anyMatch(Actor::isSuspendable)) && scenario.postExecution.size() > 0) - throw new IllegalArgumentException("Generated execution scenario for the test class with suspendable methods has non-empty post part"); - } - } - - private Verifier createVerifier(Class verifierClass, Class sequentialSpecification) throws Exception { - return verifierClass.getConstructor(Class.class).newInstance(sequentialSpecification); - } - - private ExecutionGenerator createExecutionGenerator(Class generatorClass, - CTestConfiguration testConfiguration) throws Exception { - return generatorClass.getConstructor(CTestConfiguration.class, CTestStructure.class).newInstance(testConfiguration, testStructure); - } - - private LoggingLevel getLogLevelFromAnnotation() { - LogLevel logLevelAnn = testClass.getAnnotation(LogLevel.class); - if (logLevelAnn == null) - return DEFAULT_LOG_LEVEL; - return logLevelAnn.value(); - } -} \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java deleted file mode 100644 index e892958d4..000000000 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.strategy; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.Reporter; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; -import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchCTestConfiguration; -import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchStrategy; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTestConfiguration; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressStrategy; -import org.jetbrains.kotlinx.lincheck.verifier.Verifier; -import org.objectweb.asm.ClassVisitor; - -import static org.jetbrains.kotlinx.lincheck.ReporterKt.appendIncorrectResults; - -/** - * Implementation of this class describes how to run the generated execution. - *

- * Note that strategy could run execution several times. For strategy creating - * {@link #createStrategy} method is used. It is impossible to add new strategy - * without any code change. - */ -public abstract class Strategy { - protected final ExecutionScenario scenario; - protected final Reporter reporter; - private final Verifier verifier; - - protected Strategy(ExecutionScenario scenario, Verifier verifier, Reporter reporter) { - this.scenario = scenario; - this.verifier = verifier; - this.reporter = reporter; - } - - protected void verifyResults(ExecutionResult results) { - if (!verifier.verifyResults(scenario, results)) { - StringBuilder msgBuilder = new StringBuilder("Invalid interleaving found:\n"); - appendIncorrectResults(msgBuilder, scenario, results); - throw new AssertionError(msgBuilder.toString()); - } - } - - public ClassVisitor createTransformer(ClassVisitor cv) { - throw new UnsupportedOperationException(getClass() + " runner does not transform classes"); - } - - public boolean needsTransformation() { - return false; - } - - /** - * Creates {@link Strategy} based on {@code testCfg} type. - */ - public static Strategy createStrategy(CTestConfiguration testCfg, Class testClass, - ExecutionScenario scenario, Verifier verifier, Reporter reporter) - { - if (testCfg instanceof StressCTestConfiguration) { - return new StressStrategy(testClass, scenario, verifier, - (StressCTestConfiguration) testCfg, reporter); - } else if (testCfg instanceof RandomSwitchCTestConfiguration) { - return new RandomSwitchStrategy(testClass, scenario, verifier, - (RandomSwitchCTestConfiguration) testCfg, reporter); - } - throw new IllegalArgumentException("Unknown strategy configuration type: " + testCfg.getClass()); - } - - public abstract void run() throws Exception; -} diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java deleted file mode 100644 index 4576d42a1..000000000 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.strategy.stress; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.*; -import org.jetbrains.kotlinx.lincheck.execution.*; -import org.jetbrains.kotlinx.lincheck.runner.*; -import org.jetbrains.kotlinx.lincheck.strategy.*; -import org.jetbrains.kotlinx.lincheck.verifier.*; - -import java.util.*; -import java.util.concurrent.atomic.*; - -/** - * This strategy - */ -public class StressStrategy extends Strategy { - private static final int MAX_WAIT = 1000; - private final Random random = new Random(0); - - private final int invocations; - private final Runner runner; - - private final List waits; - - public StressStrategy(Class testClass, ExecutionScenario scenario, - Verifier verifier, StressCTestConfiguration testCfg, Reporter reporter) { - super(scenario, verifier, reporter); - this.invocations = testCfg.invocationsPerIteration; - // Create waits if needed - waits = testCfg.addWaits ? new ArrayList<>() : null; - if (testCfg.addWaits) { - for (List actorsForThread : scenario.parallelExecution) { - waits.add(new int[actorsForThread.size()]); - } - } - // Create runner - runner = new ParallelThreadsRunner(scenario, this, testClass, waits); - } - - @Override - public void run() throws InterruptedException { - try { - // Run invocations - for (int invocation = 0; invocation < invocations; invocation++) { - // Specify waits if needed - if (waits != null) { - int maxWait = (int) ((float) invocation * MAX_WAIT / invocations) + 1; - for (int[] waitsForThread : waits) { - for (int i = 0; i < waitsForThread.length; i++) { - waitsForThread[i] = random.nextInt(maxWait); - } - } - } - verifyResults(runner.run()); - } - } finally { - runner.close(); - } - } -} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt deleted file mode 100644 index 994121762..000000000 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt +++ /dev/null @@ -1,144 +0,0 @@ -/*- - * #%L - * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability - -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.jetbrains.kotlinx.lincheck.verifier.* -import java.util.concurrent.locks.* -import kotlin.coroutines.* - -open class SequentialIntChannel(private val capacity: Int) : VerifierState() { - private val lock = ReentrantLock() - private val senders = ArrayList, Int>>() - private val receivers = ArrayList>() - private val buffer = ArrayList(capacity) - private var closed = false - - @InternalCoroutinesApi - suspend fun send(x: Int) { - lock.lock() - try { - if (offer(x)) return - suspendAtomicCancellableCoroutine { cont -> - senders.add(cont to x) - } - } finally { - lock.unlock() - } - } - - @InternalCoroutinesApi - fun offer(x: Int): Boolean { - lock.lock() - try { - while (true) { - if (closed) throw ClosedSendChannelException("") - if (receivers.isEmpty() && buffer.size == capacity) return false - if (receivers.isNotEmpty()) { - val r = receivers.removeAt(0) - if (r.tryResume0(x)) return true - } else { - buffer.add(x) - return true - } - } - } finally { - lock.unlock() - } - } - - @InternalCoroutinesApi - suspend fun receive(): Int { - lock.lock() - try { - val pollResult = poll() - if (pollResult !== null) return pollResult - return suspendAtomicCancellableCoroutine { cont -> - receivers.add(cont) - } - } finally { - lock.unlock() - } - } - - @InternalCoroutinesApi - suspend fun receiveOrNull(): Int? { - lock.lock() - try { - if (senders.isEmpty() && closed) return null - val pollResult = poll() - if (pollResult !== null) return pollResult - return suspendAtomicCancellableCoroutine { cont -> - receivers.add(cont) - } - } finally { - lock.unlock() - } - } - - @InternalCoroutinesApi - fun poll(): Int? { - lock.lock() - try { - if (buffer.size > 0) { - val res = buffer.removeAt(0) - while (true) { - if (senders.isEmpty()) break - val (s, x) = senders.removeAt(0) - if (s.tryResume0(Unit)) { - buffer.add(x) - break - } - } - return res - } - while (true) { - if (senders.isEmpty() && closed) throw ClosedReceiveChannelException("") - if (senders.isEmpty()) return null - val (s, x) = senders.removeAt(0) - if (s.tryResume0(Unit)) return x - } - } finally { - lock.unlock() - } - } - - fun close(): Boolean { - if (closed) return false - closed = true - receivers.forEach { - it.resumeWithException(ClosedReceiveChannelException("")) - } - receivers.clear() - return true - } - - override fun extractState() = buffer to closed -} - -@InternalCoroutinesApi -private fun CancellableContinuation.tryResume0(res: T): Boolean { - val token = tryResume(res) ?: return false - completeResume(token) - return true -} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueue.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueue.kt deleted file mode 100644 index cae7d52d8..000000000 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueue.kt +++ /dev/null @@ -1,253 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.test.verifier.quiescent - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.concurrent.atomic.AtomicLong -import java.util.concurrent.atomic.AtomicReference -import java.util.concurrent.atomic.AtomicReferenceArray - -private typealias Core = LockFreeMPSCQueueCore - -/** - * Lock-free Multiply-Producer Single-Consumer Queue from kotlinx.coroutines project. - */ -class LockFreeMPSCQuiescentConsistentQueue { - private val _cur = AtomicReference(Core(LockFreeMPSCQueueCore.INITIAL_CAPACITY)) - - // Note: it is not atomic w.r.t. remove operation (remove can transiently fail when isEmpty is false) - val isEmpty: Boolean get() = _cur.get().isEmpty - - fun close() { - while (true) { - val cur = _cur.get() - if (cur.close()) return // closed this copy - _cur.compareAndSet(cur, cur.next()) // move to next - } - } - - fun addLast(element: E): Boolean { - while (true) { - val cur = _cur.get() - when (cur.addLast(element)) { - LockFreeMPSCQueueCore.ADD_SUCCESS -> return true - LockFreeMPSCQueueCore.ADD_CLOSED -> return false - LockFreeMPSCQueueCore.ADD_FROZEN -> _cur.compareAndSet(cur, cur.next()) // move to next - } - } - } - - @Suppress("UNCHECKED_CAST") - fun removeFirstOrNull(): E? { - while (true) { - val cur = _cur.get() - val result = cur.removeFirstOrNull() - if (result !== LockFreeMPSCQueueCore.REMOVE_FROZEN) return result as E? - _cur.compareAndSet(cur, cur.next()) - } - } -} - -/** - * Lock-free Multiply-Producer Single-Consumer Queue core. - * *Note: This queue is NOT linearizable. It provides only quiescent consistency for its operations.* - * - * @see LockFreeMPSCQuiescentConsistentQueue - * @suppress **This is unstable API and it is subject to change.** - */ -internal class LockFreeMPSCQueueCore(private val capacity: Int) { - private val mask = capacity - 1 - private val _next = AtomicReference?>(null) - private val _state = AtomicLong(0L) - private val array = AtomicReferenceArray(capacity); - - init { - check(mask <= MAX_CAPACITY_MASK) - check(capacity and mask == 0) - } - - // Note: it is not atomic w.r.t. remove operation (remove can transiently fail when isEmpty is false) - val isEmpty: Boolean get() = _state.get().withState { head, tail -> head == tail } - - fun close(): Boolean { - while (true) { - val state = _state.get() - if (state and CLOSED_MASK != 0L) return true // ok - already closed - if (state and FROZEN_MASK != 0L) return false // frozen -- try next - val upd = state or CLOSED_MASK // try set closed bit - if (_state.compareAndSet(state, upd)) return true - } - } - - // ADD_CLOSED | ADD_FROZEN | ADD_SUCCESS - fun addLast(element: E): Int { - while (true) { - val state = _state.get() - if (state and (FROZEN_MASK or CLOSED_MASK) != 0L) return state.addFailReason() // cannot add - state.withState { head, tail -> - // there could be one REMOVE element beyond head that we cannot stump up, - // so we check for full queue with an extra margin of one element - if ((tail + 2) and mask == head and mask) return ADD_FROZEN // overfull, so do freeze & copy - val newTail = (tail + 1) and MAX_CAPACITY_MASK - if (_state.compareAndSet(state, state.updateTail(newTail))) { - // successfully added - array[tail and mask] = element - // could have been frozen & copied before this item was set -- correct it by filling placeholder - var cur = this - while(true) { - if (cur._state.get() and FROZEN_MASK == 0L) break // all fine -- not frozen yet - cur = cur.next().fillPlaceholder(tail, element) ?: break - } - return ADD_SUCCESS // added successfully - } - } - } - } - - private fun fillPlaceholder(index: Int, element: E): Core? { - if (array.compareAndSet(index and mask, PLACEHOLDER, element)) { - // we've corrected missing element, should check if that propagated to further copies, just in case - return this - } - // it is Ok, no need for further action - return null - } - - // SINGLE CONSUMER - // REMOVE_FROZEN | null (EMPTY) | E (SUCCESS) - fun removeFirstOrNull(): Any? { - while (true) { - val state = _state.get() - if (state and FROZEN_MASK != 0L) return REMOVE_FROZEN // frozen -- cannot modify - state.withState { head, tail -> - if ((tail and mask) == (head and mask)) return null // empty - // because queue is Single Consumer, then element == null|PLACEHOLDER can only be when add has not finished yet - val element = array[head and mask] ?: return null - if (element === PLACEHOLDER) return null // same story -- consider it not added yet - check(element !== REMOVED) { "This queue can have only one consumer" } - // tentatively remove element to let GC work - // we cannot put null into array, because copying thread could replace it with PLACEHOLDER - // and that is a disaster, so a separate REMOVED token is used here. - // Note: at most one REMOVED in the array, because single consumer. - array[head and mask] = REMOVED - val newHead = (head + 1) and MAX_CAPACITY_MASK - if (_state.compareAndSet(state, state.updateHead(newHead))) { - array[head and mask] = null // now can safely put null (state was updated) - return element // successfully removed in fast-path - } - // Slow-path for remove in case of interference - var cur = this - while (true) { - cur = cur.removeSlowPath(head, newHead) ?: return element - } - } - } - } - - private fun removeSlowPath(oldHead: Int, newHead: Int): Core? { - while (true) { - val state = _state.get() - state.withState { head, _ -> - check(head == oldHead) { "This queue can have only one consumer" } - if (state and FROZEN_MASK != 0L) { - // state was already frozen, so either old or REMOVED item could have been copied to next - val next = next() - next.array[head and next.mask] = REMOVED // make sure it is removed in new array regardless - return next // continue to correct head in next - } - if (_state.compareAndSet(state, state.updateHead(newHead))) { - array[head and mask] = null // now can safely put null (state was updated) - return null - } - } - } - } - - fun next(): LockFreeMPSCQueueCore = allocateOrGetNextCopy(markFrozen()) - - private fun markFrozen(): Long = - _state.updateAndGet { state -> - if (state and FROZEN_MASK != 0L) return@updateAndGet state // already marked - state or FROZEN_MASK - } - - private fun allocateOrGetNextCopy(state: Long): Core { - while (true) { - val next = _next.get() - if (next != null) return next // already allocated & copied - _next.compareAndSet(null, allocateNextCopy(state)) - } - } - - private fun allocateNextCopy(state: Long): Core { - val next = LockFreeMPSCQueueCore(capacity * 2) - state.withState { head, tail -> - var index = head - while (index and mask != tail and mask) { - // replace nulls with placeholders on copy - next.array[index and next.mask] = array[index and mask] ?: PLACEHOLDER - index++ - } - next._state.set(state wo FROZEN_MASK) - } - return next - } - - @Suppress("PrivatePropertyName") - internal companion object { - internal const val INITIAL_CAPACITY = 8 - - private const val CAPACITY_BITS = 30 - private const val MAX_CAPACITY_MASK = (1 shl CAPACITY_BITS) - 1 - private const val HEAD_SHIFT = 0 - private const val HEAD_MASK = MAX_CAPACITY_MASK.toLong() shl HEAD_SHIFT - private const val TAIL_SHIFT = HEAD_SHIFT + CAPACITY_BITS - private const val TAIL_MASK = MAX_CAPACITY_MASK.toLong() shl TAIL_SHIFT - - private const val FROZEN_SHIFT = TAIL_SHIFT + CAPACITY_BITS - private const val FROZEN_MASK = 1L shl FROZEN_SHIFT - private const val CLOSED_SHIFT = FROZEN_SHIFT + 1 - private const val CLOSED_MASK = 1L shl CLOSED_SHIFT - - @JvmField internal val REMOVE_FROZEN = Object() - - internal const val ADD_SUCCESS = 0 - internal const val ADD_FROZEN = 1 - internal const val ADD_CLOSED = 2 - - private val PLACEHOLDER = Object() - private val REMOVED = Object() - - private infix fun Long.wo(other: Long) = this and other.inv() - private fun Long.updateHead(newHead: Int) = (this wo HEAD_MASK) or (newHead.toLong() shl HEAD_SHIFT) - private fun Long.updateTail(newTail: Int) = (this wo TAIL_MASK) or (newTail.toLong() shl TAIL_SHIFT) - - private inline fun Long.withState(block: (head: Int, tail: Int) -> T): T { - val head = ((this and HEAD_MASK) shr HEAD_SHIFT).toInt() - val tail = ((this and TAIL_MASK) shr TAIL_SHIFT).toInt() - return block(head, tail) - } - - // FROZEN | CLOSED - private fun Long.addFailReason(): Int = if (this and CLOSED_MASK != 0L) ADD_CLOSED else ADD_FROZEN - } -} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueueTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueueTest.java deleted file mode 100644 index 6c6794e78..000000000 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQuiescentConsistentQueueTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.test.verifier.quiescent; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jetbrains.kotlinx.lincheck.verifier.quiescent.QuiescentConsistencyVerifier; -import org.jetbrains.kotlinx.lincheck.verifier.quiescent.QuiescentConsistent; -import org.junit.Test; - -@StressCTest(verifier = QuiescentConsistencyVerifier.class, requireStateEquivalenceImplCheck = false) -@OpGroupConfig(name = "consumer", nonParallel = true) -public class LockFreeMPSCQuiescentConsistentQueueTest { - private final LockFreeMPSCQuiescentConsistentQueue q = new LockFreeMPSCQuiescentConsistentQueue<>(); - - @Operation(group = "consumer") - @QuiescentConsistent - public Integer removeFirstOrNull() { - return q.removeFirstOrNull(); - } - - @Operation - public boolean addLast(@Param(gen = IntGen.class) Integer val) { - return q.addLast(val); - } - - @Operation(runOnce = true) - @QuiescentConsistent - public void close() { - q.close(); - } - - @Test - public void test() { - LinChecker.check(LockFreeMPSCQuiescentConsistentQueueTest.class); - } -} \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulation.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulation.kt deleted file mode 100644 index f755079ed..000000000 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulation.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * #%L - * lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.test.verifier.serializability - -import java.util.Collections.shuffle - -data class SerializableQueueSimulation( - private val queue: ArrayList = ArrayList(), - private val pushQueue: ArrayList = ArrayList() -) { - fun push(item: T) = synchronized(this) { - pushQueue += item - } - - fun pop(): T? = synchronized(this) { - if (queue.isEmpty()) { - shuffle(pushQueue) - queue.addAll(pushQueue) - pushQueue.clear() - } - return if (queue.isEmpty()) null else queue.removeAt(0) - } -} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt deleted file mode 100644 index 3cb38f093..000000000 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program 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 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -package org.jetbrains.kotlinx.lincheck.test.verifier.serializability - -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.annotations.* -import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.jetbrains.kotlinx.lincheck.verifier.SerializabilityVerifier -import org.junit.Test - - -@StressCTest(actorsBefore = 2, - threads = 2, actorsPerThread = 4, - actorsAfter = 2, - verifier = SerializabilityVerifier::class) -class SerializableQueueSimulationTest { - val q = SerializableQueueSimulation() - - @Operation - fun push(@Param(gen = IntGen::class) item: Int) = q.push(item) - - @Operation - fun pop(): Int? = q.pop() - - @Test - fun test() = LinChecker.check(SerializableQueueSimulationTest::class.java) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SerializableQueueSimulationTest - - if (q != other.q) return false - - return true - } - - override fun hashCode(): Int { - return q.hashCode() - } -} diff --git a/pom.xml b/pom.xml index fe6873cf9..5422c6df9 100755 --- a/pom.xml +++ b/pom.xml @@ -4,11 +4,11 @@ 4.0.0 org.jetbrains.kotlinx - lincheck-all - pom + lincheck + jar 2.7-SNAPSHOT - lin-check (all) + Lincheck Framework for testing concurrent data structures 2019 @@ -23,18 +23,13 @@ UTF-8 lgpl_v3 false - 2019 + 2020 1.3.50 + 1.3.5 0.9.17 7.1 - false - - lincheck - libtest - - @@ -93,7 +88,6 @@ compile - ${project.basedir}/src/main/java @@ -178,12 +172,6 @@ true - - maven-deploy-plugin - - ${deploy.skip} - - @@ -248,12 +236,39 @@ kotlin-stdlib ${kotlin.version} + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx.coroutines.version} + + + org.ow2.asm + asm-commons + ${asm.version} + + + org.ow2.asm + asm-util + ${asm.version} + + junit junit 4.12 test + + org.jctools + jctools-core + 2.1.0 + test + diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt similarity index 98% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt index 5c243cf89..526284ffc 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -37,7 +37,7 @@ data class Actor @JvmOverloads constructor( val handledExceptions: List>, val cancelOnSuspension: Boolean = false ) { - override fun toString() = cancellableMark + method.name + arguments.joinToString(prefix = "(", postfix = ")", separator = ",") { it.toString() } + override fun toString() = cancellableMark + method.name + arguments.joinToString(prefix = "(", postfix = ")", separator = ", ") { it.toString() } private val cancellableMark get() = (if (cancelOnSuspension) "*" else "") val handlesExceptions = handledExceptions.isNotEmpty() diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java b/src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java similarity index 94% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java index 04e439c64..f91a50d85 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/CTestConfiguration.java @@ -22,8 +22,8 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.execution.ExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchCTest; import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchCTestConfiguration; import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; @@ -81,7 +81,9 @@ protected CTestConfiguration(Class testClass, int iterations, int threads, in this.sequentialSpecification = sequentialSpecification; } - static List createFromTestClass(Class testClass) { + protected abstract Strategy createStrategy(Class testClass, ExecutionScenario scenario, Verifier verifier); + + static List createFromTestClassAnnotations(Class testClass) { Stream stressConfigurations = Arrays.stream(testClass.getAnnotationsByType(StressCTest.class)) .map(ann -> new StressCTestConfiguration(testClass, ann.iterations(), ann.threads(), ann.actorsPerThread(), ann.actorsBefore(), ann.actorsAfter(), diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CTestStructure.java b/src/main/java/org/jetbrains/kotlinx/lincheck/CTestStructure.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CTestStructure.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/CTestStructure.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java b/src/main/java/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt new file mode 100644 index 000000000..f0be110ab --- /dev/null +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -0,0 +1,192 @@ +/* + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.CTestConfiguration.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* + +/** + * This class runs concurrent tests. + */ +class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { + private val testStructure = CTestStructure.getFromTestClass(testClass) + private val testConfigurations: List + private val reporter: Reporter + + init { + val logLevel = options?.logLevel ?: testClass.getAnnotation(LogLevel::class.java)?.value ?: DEFAULT_LOG_LEVEL + reporter = Reporter(logLevel) + testConfigurations = if (options != null) listOf(options.createTestConfigurations(testClass)) + else createFromTestClassAnnotations(testClass) + } + + /** + * @throws LincheckAssertionError if the testing data structure is incorrect. + */ + fun check() { + val failedIteration = checkImpl() ?: return + throw LincheckAssertionError(failedIteration) + } + + /** + * @return TestReport with information about concurrent test run. + */ + internal fun checkImpl(): FailedIteration? { + check(testConfigurations.isNotEmpty()) { "No Lincheck test configuration to run" } + for (testCfg in testConfigurations) { + val failedIteration = testCfg.checkImpl() + if (failedIteration != null) return failedIteration + } + return null + } + + private fun CTestConfiguration.checkImpl(): FailedIteration? { + val exGen = createExecutionGenerator() + val verifier = createVerifier() + repeat(iterations) { iteration -> + val scenario = exGen.nextExecution() + scenario.validate() + reporter.logIteration(iteration, iterations, scenario) + val failedIteration = scenario.run(this, verifier) + if (failedIteration != null) { + val minimizedFailedIteration = if (!minimizeFailedScenario) failedIteration + else failedIteration.minimize(this, verifier) + reporter.logFailedIteration(minimizedFailedIteration) + return minimizedFailedIteration + } + } + return null + } + + // Tries to minimize the specified failing scenario to make the error easier to understand. + // The algorithm is greedy: it tries to remove one actor from the scenario and checks + // whether a test with the modified one fails with error as well. If it fails, + // then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. + // Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. + // Thus, the algorithm works in the linear time of the total number of actors. + private fun FailedIteration.minimize(testCfg: CTestConfiguration, verifier: Verifier): FailedIteration { + reporter.logScenarioMinimization(scenario) + for (i in scenario.parallelExecution.indices) { + for (j in scenario.parallelExecution[i].indices) { + val newScenario = scenario.copy() + newScenario.parallelExecution[i].removeAt(j) + if (newScenario.parallelExecution[i].isEmpty()) newScenario.parallelExecution.removeAt(i) // remove empty thread + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + } + for (i in scenario.initExecution.indices) { + val newScenario = scenario.copy() + newScenario.initExecution.removeAt(i) + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + for (i in scenario.postExecution.indices) { + val newScenario = scenario.copy() + newScenario.postExecution.removeAt(i) + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + return this + } + + private fun ExecutionScenario.tryMinimize(testCfg: CTestConfiguration, verifier: Verifier) = + if (isValid) run(testCfg, verifier) else null + + private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): FailedIteration? { + val strategy = testCfg.createStrategy(testClass, this, verifier) + return strategy.run() + } + + private fun ExecutionScenario.copy() = ExecutionScenario( + ArrayList(initExecution), + parallelExecution.map { ArrayList(it) }, + ArrayList(postExecution) + ) + + private val ExecutionScenario.isValid: Boolean + get() = !isParallelPartEmpty && + (!hasSuspendableActors() || (!hasSuspendableActorsInInitPart && !hasPostPartAndSuspendableActors)) + + private fun ExecutionScenario.validate() { + require(!isParallelPartEmpty) { + "The generated scenario has empty parallel part" + } + if (hasSuspendableActors()) { + require(!hasSuspendableActorsInInitPart) { + "The generated scenario for the test class with suspendable methods contains suspendable actors in initial part" + } + require(!hasPostPartAndSuspendableActors) { + "The generated scenario for the test class with suspendable methods has non-empty post part" + } + } + } + + private val ExecutionScenario.hasSuspendableActorsInInitPart get() = + initExecution.stream().anyMatch(Actor::isSuspendable) + private val ExecutionScenario.hasPostPartAndSuspendableActors get() = + (parallelExecution.stream().anyMatch { actors -> actors.stream().anyMatch { it.isSuspendable } } && postExecution.size > 0) + private val ExecutionScenario.isParallelPartEmpty get() = + parallelExecution.map { it.size }.sum() == 0 + + + private fun CTestConfiguration.createVerifier() = + verifierClass.getConstructor(Class::class.java).newInstance(sequentialSpecification).also { + if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() + } + + private fun CTestConfiguration.createExecutionGenerator() = + generatorClass.getConstructor( + CTestConfiguration::class.java, + CTestStructure::class.java + ).newInstance(this, testStructure) + + // This companion object is used for backwards compatibility. + companion object { + /** + * Runs the specified concurrent tests. If [options] is null, the provided on + * the testing class `@...CTest` annotations are used to specify the test parameters. + * + * @throws AssertionError if any of the tests fails. + */ + @JvmOverloads + @JvmStatic + fun check(testClass: Class<*>, options: Options<*, *>? = null) { + LinChecker(testClass, options).check() + } + } +} + + +/** + * This is a short-cut for the following code: + * ``` + * val options = ... + * LinChecker.check(testClass, options) + * ``` + */ +fun > O.check(testClass: Class<*>) = LinChecker.check(testClass, this) + +internal fun > O.checkImpl(testClass: Class<*>) = LinChecker(testClass, this).checkImpl() \ No newline at end of file diff --git a/libtest/src/main/java/psy/lob/saw/queues/common/UnsafeAccess.java b/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt similarity index 62% rename from libtest/src/main/java/psy/lob/saw/queues/common/UnsafeAccess.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt index fd03b67d6..26fa98c39 100644 --- a/libtest/src/main/java/psy/lob/saw/queues/common/UnsafeAccess.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt @@ -1,10 +1,8 @@ -package psy.lob.saw.queues.common; - -/* +/*- * #%L - * libtest + * Lincheck * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC + * Copyright (C) 2019 - 2020 JetBrains s.r.o. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -21,21 +19,14 @@ * . * #L% */ +package org.jetbrains.kotlinx.lincheck -import java.lang.reflect.Field; - -import sun.misc.Unsafe; +import org.jetbrains.kotlinx.lincheck.strategy.* +import java.lang.AssertionError +import java.lang.StringBuilder -public class UnsafeAccess { - public static final Unsafe UNSAFE; - static { - try { - Field field = Unsafe.class.getDeclaredField("theUnsafe"); - field.setAccessible(true); - UNSAFE = (Unsafe) field.get(null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } +class LincheckAssertionError( + failedIteration: FailedIteration +) : AssertionError(failedIteration.createMessage()) -} +private fun FailedIteration.createMessage() = StringBuilder().appendFailedIteration(this).toString() \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java b/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java similarity index 99% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/Options.java index 0a5faafb5..e1b7e34e7 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java @@ -164,5 +164,4 @@ public OPT sequentialSpecification(Class clazz) { this.sequentialSpecification = clazz; return (OPT) this; } - } diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt similarity index 66% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt index 431e545b3..9fe142a23 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -22,35 +22,40 @@ package org.jetbrains.kotlinx.lincheck +import org.jetbrains.kotlinx.lincheck.LoggingLevel.* import org.jetbrains.kotlinx.lincheck.execution.* -import java.io.PrintStream +import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import java.io.* class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: PrintStream = System.out) { - fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = synchronized(this) { - if (logLevel > LoggingLevel.INFO) return + fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = log(INFO) { StringBuilder("\n= Iteration $iteration / $maxIterations =\n").run { appendExecutionScenario(scenario) out.println(this) } } - fun logScenarioMinimization(scenario: ExecutionScenario) { - if (logLevel > LoggingLevel.INFO) return + fun logFailedIteration(failedIteration: FailedIteration) = log(INFO) { + StringBuilder().appendFailedIteration(failedIteration) + } + + fun logScenarioMinimization(scenario: ExecutionScenario) = log(INFO) { StringBuilder("\nInvalid interleaving found, trying to minimize the scenario below:\n").run { appendExecutionScenario(scenario) out.println(this) } } - inline fun log(logLevel: LoggingLevel, crossinline msg: () -> String) { + private inline fun log(logLevel: LoggingLevel, crossinline msg: () -> Any): Unit = synchronized(this) { if (this.logLevel > logLevel) return out.println(msg()) } } -@JvmField val DEFAULT_LOG_LEVEL = LoggingLevel.ERROR +@JvmField val DEFAULT_LOG_LEVEL = ERROR enum class LoggingLevel { - DEBUG, INFO, ERROR + INFO, ERROR } private fun printInColumns(groupedObjects: List>): String { @@ -130,21 +135,52 @@ private fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario) { } } -fun StringBuilder.appendIncorrectResults(scenario: ExecutionScenario, results: ExecutionResult) { +internal fun StringBuilder.appendFailedIteration(failedIteration: FailedIteration): StringBuilder = + when (failedIteration) { + is IncorrectResultsFailedIteration -> appendIncorrectResultsFailedIteration(failedIteration) + is DeadlockedWithDumpFailedIteration -> appendDeadlockedWithDumpFailedIteration(failedIteration) + is UnexpectedExceptionFailedIteration -> appendUnexpectedException(failedIteration) + } + +private fun StringBuilder.appendUnexpectedException(failedIteration: UnexpectedExceptionFailedIteration): StringBuilder { + appendln("= The execution failed with an unexpected exception =") + appendExecutionScenario(failedIteration.scenario) + val sw = StringWriter() + failedIteration.exception.printStackTrace(PrintWriter(sw)) + appendln(sw.toString()) + return this +} + +private fun StringBuilder.appendDeadlockedWithDumpFailedIteration(failedIteration: DeadlockedWithDumpFailedIteration): StringBuilder { + appendln("= The execution has hung, see the thread dump =") + appendExecutionScenario(failedIteration.scenario) + for ((t, stackTrace) in failedIteration.threadDump) { + val threadNumber = if (t is ParallelThreadsRunner.TestThread) t.iThread else "?" + appendln("Thread-$threadNumber:") + for (ste in stackTrace) { + if (ste.className.startsWith("org.jetbrains.kotlinx.lincheck.runner.")) break + appendln("\t$ste") + } + } + return this +} + +private fun StringBuilder.appendIncorrectResultsFailedIteration(failedIteration: IncorrectResultsFailedIteration): StringBuilder { appendln("= Invalid execution results =") - if (scenario.initExecution.isNotEmpty()) { + if (failedIteration.scenario.initExecution.isNotEmpty()) { appendln("Init part:") - appendln(uniteActorsAndResultsLinear(scenario.initExecution, results.initResults)) + appendln(uniteActorsAndResultsLinear(failedIteration.scenario.initExecution, failedIteration.results.initResults)) } appendln("Parallel part:") - val parallelExecutionData = uniteParallelActorsAndResults(scenario.parallelExecution, results.parallelResultsWithClock) + val parallelExecutionData = uniteParallelActorsAndResults(failedIteration.scenario.parallelExecution, failedIteration.results.parallelResultsWithClock) append(printInColumns(parallelExecutionData)) - if (scenario.postExecution.isNotEmpty()) { + if (failedIteration.scenario.postExecution.isNotEmpty()) { appendln() appendln("Post part:") - append(uniteActorsAndResultsLinear(scenario.postExecution, results.postResults)) + append(uniteActorsAndResultsLinear(failedIteration.scenario.postExecution, failedIteration.results.postResults)) } - if (results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) + if (failedIteration.results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) appendln("\n---\nvalues in \"[..]\" brackets indicate the number of completed operations \n" + "in each of the parallel threads seen at the beginning of the current operation\n---") + return this } \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Result.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/Result.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Result.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/Result.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java b/src/main/java/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt similarity index 98% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt index 6df049d6c..8d9577d23 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -80,7 +80,7 @@ internal fun executeActor( val m = getMethod(specification, actor) val args = if (actor.isSuspendable) actor.arguments + completion else actor.arguments val res = m.invoke(specification, *args.toTypedArray()) - return if (m.returnType.isAssignableFrom(Void.TYPE)) VoidResult else createLinCheckResult(res) + return if (m.returnType.isAssignableFrom(Void.TYPE)) VoidResult else createLincheckResult(res) } catch (invE: Throwable) { val eClass = (invE.cause ?: invE).javaClass.normalize() for (ec in actor.handledExceptions) { @@ -124,7 +124,7 @@ private fun getMethod(instance: Any, actor: Actor): Method { * Success values of [kotlin.Result] instances are represented as either [VoidResult] or [ValueResult]. * Failure values of [kotlin.Result] instances are represented as [ExceptionResult]. */ -internal fun createLinCheckResult(res: Any?, wasSuspended: Boolean = false) = when { +internal fun createLincheckResult(res: Any?, wasSuspended: Boolean = false) = when { (res != null && res.javaClass.isAssignableFrom(Void.TYPE)) || res is Unit -> if (wasSuspended) SuspendedVoidResult else VoidResult res != null && res is Throwable -> ExceptionResult.create(res.javaClass, wasSuspended) res === COROUTINE_SUSPENDED -> Suspended diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java similarity index 90% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java index 55df03824..abe3cd2b8 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java @@ -31,8 +31,8 @@ import java.lang.annotation.Target; /** - * This annotation should be added to a test class - * to specify logging level. By default {@link LoggingLevel#ERROR} is used. + * This annotation should be added to a test class to specify the logging level. + * By default, {@link LoggingLevel#ERROR} is used. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java similarity index 97% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java index 06c0b59ce..19eac8359 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java @@ -34,7 +34,7 @@ public @interface Operation { /** * Binds the arguments of this operations with the specified {@link Param parameter configurations} - * by theirs {@link Param#name()} names + * by theirs {@link Param#name()} names. */ String[] params() default {}; diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Param.java b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Param.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Param.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Param.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.java b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java similarity index 99% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java index a52874a11..0e21fe7a9 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.execution; - /* * #%L * Lincheck @@ -10,17 +8,18 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.execution; import org.jetbrains.kotlinx.lincheck.Actor; import org.jetbrains.kotlinx.lincheck.strategy.Strategy; diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt similarity index 92% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt index 796bc81dd..9f8bb31ef 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt @@ -28,7 +28,9 @@ data class HBClock(val clock: IntArray) { val empty: Boolean get() = clock.all { it == 0 } operator fun get(i: Int) = clock[i] - override fun toString()= clock.joinToString(prefix = "[", separator = ",", postfix = "]") + override fun toString() = clock.joinToString(prefix = "[", separator = ",", postfix = "]") { + if (it == 0) "-" else "$it" + } override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java b/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java b/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt new file mode 100644 index 000000000..7605a8b1a --- /dev/null +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -0,0 +1,50 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Represents results for invocations, see [Runner.run]. + */ +sealed class InvocationResult + +/** + * The invocation completed successfully, the output [results] are provided. + */ +data class CompletedInvocationResult( + val results: ExecutionResult +) : InvocationResult() + +/** + * Indicates that the invocation has get into deadlock or livelock. + */ +data class DeadlockInvocationResult( + val threadDump: Map> +) : InvocationResult() + +/** + * The invocation has finished with an unexpected exception. + */ +class UnexpectedExceptionInvocationResult( + val exception: Throwable +) : InvocationResult() \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt similarity index 89% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 4c6625de0..2f06d2bcd 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -42,11 +42,10 @@ private typealias SuspensionPointResultWithContinuation = AtomicReference, waits: List? -) : Runner(scenario, strategy, testClass) { +) : Runner(strategy, testClass) { private lateinit var testInstance: Any private val executor = newFixedThreadPool(scenario.threads, ParallelThreadsRunner::TestThread) @@ -66,7 +65,7 @@ open class ParallelThreadsRunner( private var suspensionPointResults = MutableList(scenario.threads) { NoResult } private val uninitializedThreads = AtomicInteger(scenario.threads) // for threads synchronization - private var spinningTimeBeforeYield = 50_000 // # of loop cycles + private var spinningTimeBeforeYield = 1000 // # of loop cycles private var yieldInvokedInOnStart = false /** @@ -91,7 +90,7 @@ open class ParallelThreadsRunner( if (resWithCont.get() === null) completedOrSuspendedThreads.decrementAndGet() // write function's final result - suspensionPointResults[threadId] = createLinCheckResult(result, wasSuspended = true) + suspensionPointResults[threadId] = createLincheckResult(result, wasSuspended = true) } } } @@ -158,7 +157,7 @@ open class ParallelThreadsRunner( val cont = t.cont.also { t.cont = null } if (actor.cancelOnSuspension && cont !== null && cont.cancelByLincheck()) Cancelled else waitAndInvokeFollowUp(threadId, actorId) - } else createLinCheckResult(res) + } else createLincheckResult(res) if (isLastActor(threadId, actorId) && finalResult !== Suspended) completedOrSuspendedThreads.incrementAndGet() suspensionPointResults[threadId] = NoResult @@ -191,27 +190,17 @@ open class ParallelThreadsRunner( return suspensionPointResults[threadId] } - override fun run(): ExecutionResult { + override fun run(): InvocationResult { reset() val initResults = scenario.initExecution.map { initActor -> executeActor(testInstance, initActor) } testThreadExecutions.map { executor.submit(it) }.forEach { future -> try { future.get(10, TimeUnit.SECONDS) } catch (e: TimeoutException) { - val stackTraces = Thread.getAllStackTraces().filter { (t, _) -> t is TestThread } - val msgBuilder = StringBuilder() - msgBuilder.appendln("The execution has hung, see the thread dump:") - for ((t, stackTrace) in stackTraces) { - t as TestThread - msgBuilder.appendln("Thread-${t.iThread}:") - for (ste in stackTrace) { - if (ste.className.startsWith("org.jetbrains.kotlinx.lincheck.runner.")) break - msgBuilder.appendln("\t$ste") - } - msgBuilder.appendln() - } - Thread.getAllStackTraces().map { it.key }.filterIsInstance().forEach { it.stop() } - throw AssertionError(msgBuilder.toString()) + val threadDump = Thread.getAllStackTraces().filter { (t, _) -> t is TestThread } + return DeadlockInvocationResult(threadDump) + } catch (e: ExecutionException) { + return UnexpectedExceptionInvocationResult(e.cause!!) } } val parallelResultsWithClock = testThreadExecutions.map { ex -> @@ -228,7 +217,8 @@ open class ParallelThreadsRunner( executeActor(testInstance, postActor, dummyCompletion).also { postPartSuspended = it.wasSuspended } } } - return ExecutionResult(initResults, parallelResultsWithClock, postResults) + val results = ExecutionResult(initResults, parallelResultsWithClock, postResults) + return CompletedInvocationResult(results) } override fun onStart(iThread: Int) { @@ -264,4 +254,4 @@ open class ParallelThreadsRunner( } } -private const val MAX_SPINNING_TIME_BEFORE_YIELD = 10_000_000 \ No newline at end of file +private const val MAX_SPINNING_TIME_BEFORE_YIELD = 2_000_000 \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java similarity index 80% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java index b2a8686ea..9962f32aa 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java @@ -1,34 +1,34 @@ -package org.jetbrains.kotlinx.lincheck.runner; - /* * #%L * Lincheck * %% * Copyright (C) 2015 - 2018 Devexperts, LLC + * Copyright (C) 2019-2020 JetBrains s.r.o. * %% * This program 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 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ -import org.jetbrains.kotlinx.lincheck.ExecutionClassLoader; -import org.jetbrains.kotlinx.lincheck.TransformationClassLoader; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; -import org.jetbrains.kotlinx.lincheck.strategy.Strategy; -import org.objectweb.asm.ClassVisitor; -import java.util.concurrent.atomic.AtomicInteger; +package org.jetbrains.kotlinx.lincheck.runner; + +import org.jetbrains.kotlinx.lincheck.*; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; +import org.objectweb.asm.*; + +import java.util.concurrent.atomic.*; /** * Runner determines how to run your concurrent test. In order to support techniques @@ -40,11 +40,12 @@ public abstract class Runner { protected final ExecutionScenario scenario; protected final Class testClass; public final ExecutionClassLoader classLoader; + protected final AtomicInteger completedOrSuspendedThreads = new AtomicInteger(0); - protected Runner(ExecutionScenario scenario, Strategy strategy, Class testClass) { - this.scenario = scenario; - classLoader = (this.needsTransformation() || strategy.needsTransformation()) ? + protected Runner(Strategy strategy, Class testClass) { + this.scenario = strategy.getScenario(); + this.classLoader = (this.needsTransformation() || strategy.needsTransformation()) ? new TransformationClassLoader(strategy, this) : new ExecutionClassLoader(); this.testClass = loadClass(testClass.getTypeName()); } @@ -79,10 +80,9 @@ public boolean needsTransformation() { } /** - * Runs next invocation - * @return the obtained results + * Runs the next invocation. */ - public abstract ExecutionResult run() throws InterruptedException; + public abstract InvocationResult run(); /** * This method is invoked by every test thread as the first operation. diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java similarity index 97% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java index bc1988b28..9a32c4f0c 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java @@ -23,8 +23,6 @@ import org.jetbrains.kotlinx.lincheck.Result; -import java.util.*; -import java.util.concurrent.Callable; /** * Instance of this class represents the test execution for ONE thread. Several instances should be ran in parallel. diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt new file mode 100644 index 000000000..7b6529693 --- /dev/null +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt @@ -0,0 +1,53 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.strategy + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* + +sealed class FailedIteration( + val scenario: ExecutionScenario +) { + override fun toString() = StringBuilder().appendFailedIteration(this).toString() +} + +internal class IncorrectResultsFailedIteration( + scenario: ExecutionScenario, + val results: ExecutionResult +) : FailedIteration(scenario) + +internal class DeadlockedWithDumpFailedIteration( + scenario: ExecutionScenario, + val threadDump: Map> +) : FailedIteration(scenario) + +internal class UnexpectedExceptionFailedIteration( + scenario: ExecutionScenario, + val exception: Throwable +) : FailedIteration(scenario) + +internal fun InvocationResult.toFailedIteration(scenario: ExecutionScenario) = when (this) { + is DeadlockInvocationResult -> DeadlockedWithDumpFailedIteration(scenario, threadDump) + is UnexpectedExceptionInvocationResult -> UnexpectedExceptionFailedIteration(scenario, exception) + else -> error("Unexpected invocation result type: ${this.javaClass.simpleName}") +} \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java similarity index 88% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java index bcb771c9c..b282b41d0 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java @@ -10,25 +10,21 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ -import org.jetbrains.kotlinx.lincheck.Reporter; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; -import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner; -import org.jetbrains.kotlinx.lincheck.runner.Runner; -import org.jetbrains.kotlinx.lincheck.verifier.Verifier; -import org.objectweb.asm.ClassVisitor; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.runner.*; +import org.objectweb.asm.*; /** * This is an abstract class for all managed strategies. @@ -45,10 +41,10 @@ public abstract class ManagedStrategy extends Strategy { private final Runner runner; private ManagedStrategyTransformer transformer; - protected ManagedStrategy(Class testClass, ExecutionScenario scenario, Verifier verifier, Reporter reporter) { - super(scenario, verifier, reporter); + protected ManagedStrategy(Class testClass, ExecutionScenario scenario) { + super(scenario); nThreads = scenario.parallelExecution.size(); - runner = new ParallelThreadsRunner(scenario, this, testClass, null) { + runner = new ParallelThreadsRunner(this, testClass, null) { @Override public void onStart(int iThread) { super.onStart(iThread); @@ -75,9 +71,9 @@ public boolean needsTransformation() { } @Override - public final void run() throws Exception { + public final FailedIteration run() { try { - runImpl(); + return runImpl(); } finally { runner.close(); } @@ -88,7 +84,7 @@ public final void run() throws Exception { * * @return invocation results for each executed actor. */ - protected final ExecutionResult runInvocation() throws InterruptedException { + protected final InvocationResult runInvocation() { return runner.run(); } @@ -103,10 +99,8 @@ protected final StackTraceElement getLocationDescription(int codeLocation) { /** * This method implements the strategy logic - * - * @throws Exception an occurred exception (at least by {@link Verifier}) during the testing */ - protected abstract void runImpl() throws Exception; + protected abstract FailedIteration runImpl(); // == LISTENING EVENTS == diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyHolder.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyHolder.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyHolder.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyHolder.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyTransformer.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyTransformer.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyTransformer.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategyTransformer.java diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt new file mode 100644 index 000000000..2ce431fd3 --- /dev/null +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -0,0 +1,43 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.strategy + +import org.jetbrains.kotlinx.lincheck.execution.* +import org.objectweb.asm.* + +/** + * Implementation of this class describes how to run the generated execution. + * + * Note that strategy could run execution several times. For strategy creating + * [.createStrategy] method is used. It is impossible to add new strategy + * without any code change. + */ +abstract class Strategy protected constructor( + val scenario: ExecutionScenario +) { + open fun needsTransformation() = false + open fun createTransformer(cv: ClassVisitor?): ClassVisitor { + throw UnsupportedOperationException("$javaClass strategy does not transform classes") + } + + abstract fun run(): FailedIteration? +} diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTest.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTest.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTest.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTest.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java similarity index 86% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java index 13ef62323..a44135a2b 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchCTestConfiguration.java @@ -23,7 +23,8 @@ */ import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionGenerator; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; import org.jetbrains.kotlinx.lincheck.verifier.Verifier; /** @@ -42,4 +43,9 @@ public RandomSwitchCTestConfiguration(Class testClass, int iterations, int th requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification); this.invocationsPerIteration = invocationsPerIteration; } + + @Override + protected Strategy createStrategy(Class testClass, ExecutionScenario scenario, Verifier verifier) { + return new RandomSwitchStrategy(this, testClass, scenario, verifier); + } } diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchOptions.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchOptions.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchOptions.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchOptions.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java similarity index 58% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java index ca2aee2c2..0a7602b7f 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java @@ -22,10 +22,13 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.Reporter; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; -import org.jetbrains.kotlinx.lincheck.strategy.ManagedStrategy; -import org.jetbrains.kotlinx.lincheck.verifier.Verifier; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.runner.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; +import org.jetbrains.kotlinx.lincheck.verifier.*; + +import static org.jetbrains.kotlinx.lincheck.strategy.FailedIterationKt.toFailedIteration; + /** * This managed strategy switches current thread to a random one with the specified probability. @@ -35,18 +38,28 @@ */ public class RandomSwitchStrategy extends ManagedStrategy { private final int invocations; + private final Verifier verifier; - public RandomSwitchStrategy(Class testClass, ExecutionScenario scenario, - Verifier verifier, RandomSwitchCTestConfiguration testCfg, Reporter reporter) - { - super(testClass, scenario, verifier, reporter); + public RandomSwitchStrategy(RandomSwitchCTestConfiguration testCfg, Class testClass, + ExecutionScenario scenario, Verifier verifier) { + super(testClass, scenario); this.invocations = testCfg.invocationsPerIteration; + this.verifier = verifier; } @Override - protected void runImpl() throws Exception { - for (int i = 0; i < invocations; i++) - verifyResults(runInvocation()); + protected FailedIteration runImpl() { + for (int i = 1; i < invocations; i++) { + InvocationResult ir = runInvocation(); + if (ir instanceof CompletedInvocationResult) { + ExecutionResult results = ((CompletedInvocationResult) ir).getResults(); + if (!verifier.verifyResults(getScenario(), results)) + return new IncorrectResultsFailedIteration(getScenario(), results); + } else { + return toFailedIteration(ir, getScenario()); + } + } + return null; } @Override diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java similarity index 86% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java index 5098debf3..ac19571c3 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.java @@ -23,7 +23,8 @@ */ import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionGenerator; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; import org.jetbrains.kotlinx.lincheck.verifier.Verifier; /** @@ -45,4 +46,9 @@ public StressCTestConfiguration(Class testClass, int iterations, int threads, this.invocationsPerIteration = invocationsPerIteration; this.addWaits = addWaits; } + + @Override + protected Strategy createStrategy(Class testClass, ExecutionScenario scenario, Verifier verifier) { + return new StressStrategy(this, testClass, scenario, verifier); + } } diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.java diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt new file mode 100644 index 000000000..412568975 --- /dev/null +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -0,0 +1,81 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.strategy.stress + +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import java.util.* + +class StressStrategy( + testCfg: StressCTestConfiguration, + testClass: Class<*>, + scenario: ExecutionScenario, + private val verifier: Verifier +) : Strategy(scenario) { + private val random = Random(0) + private val invocations = testCfg.invocationsPerIteration + private val runner: Runner + private val waits: MutableList? + + init { + // Create waits if needed + waits = if (testCfg.addWaits) ArrayList() else null + if (testCfg.addWaits) { + for (actorsForThread in scenario.parallelExecution) { + waits!!.add(IntArray(actorsForThread.size)) + } + } + // Create runner + runner = ParallelThreadsRunner(this, testClass, waits) + } + + override fun run(): FailedIteration? { + try { + // Run invocations + for (invocation in 0 until invocations) { + // Specify waits if needed + if (waits != null) { + val maxWait = (invocation.toFloat() * MAX_WAIT / invocations).toInt() + 1 + for (waitsForThread in waits) { + for (i in waitsForThread.indices) { + waitsForThread[i] = random.nextInt(maxWait) + } + } + } + when (val ir = runner.run()) { + is CompletedInvocationResult -> { + if (!verifier.verifyResults(scenario, ir.results)) + return IncorrectResultsFailedIteration(scenario, ir.results) + } + else -> return ir.toFailedIteration(scenario) + } + } + return null + } finally { + runner.close() + } + } +} + +private const val MAX_WAIT = 1000 diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java similarity index 89% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java index 88e297727..234da2f33 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.verifier; - /* * #%L * Lincheck @@ -10,20 +8,20 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.verifier; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; +import org.jetbrains.kotlinx.lincheck.execution.*; /** * This verifier does nothing and could be used for performance benchmarking. diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt similarity index 99% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index c1acc4422..8efcdc14b 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -205,7 +205,7 @@ class LTS(sequentialSpecification: Class<*>) { resumedOperations[ticket]!!.contWithSuspensionPointRes.second }) resumedOperations.remove(ticket) - createLinCheckResult(finalRes, wasSuspended = true) + createLincheckResult(finalRes, wasSuspended = true) } CANCELLATION -> { check(continuationsMap[Operation(this.actor, this.ticket, REQUEST)]!!.cancelByLincheck()) { "Error, should be able to cancel" } diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt similarity index 100% rename from lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt new file mode 100644 index 000000000..57bcebce1 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt @@ -0,0 +1,58 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* +import kotlin.reflect.* + +abstract class AbstractLincheckTest( + private vararg val expectedErrors: KClass +) : VerifierState() { + open fun > O.customize() {} + override fun extractState(): Any = System.identityHashCode(this) + + private fun > O.runInternalTest() { + val failedIteration: FailedIteration? = checkImpl(this@AbstractLincheckTest::class.java) + if (failedIteration === null) { + assert(expectedErrors.isEmpty()) { + "This test should fail, but no error has been occurred (see the logs for details)" + } + } else { + assert(expectedErrors.contains(failedIteration::class)) { + "This test has been failed with an unexpected error: \n $failedIteration" + } + } + } + + @Test(timeout = 100_000) + fun testWithStressStrategy(): Unit = StressOptions().run { + invocationsPerIteration(10_000) + iterations(30) + minimizeFailedScenario(false) + customize() + runInternalTest() + } +} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt similarity index 88% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt index a1afe77e4..e696c539a 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt @@ -21,15 +21,14 @@ */ package org.jetbrains.kotlinx.lincheck.test -import org.jetbrains.kotlinx.lincheck.LinChecker +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.annotations.Param -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* import org.junit.Assert.* -import org.junit.Test -import java.lang.AssertionError @Param(name = "key", gen = IntGen::class, conf = "1:5") class FailedScenarioMinimizationTest: VerifierState() { diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt similarity index 73% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt index c29126ec6..b1685980b 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt @@ -23,17 +23,20 @@ package org.jetbrains.kotlinx.lincheck.test import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.junit.* +import org.jetbrains.kotlinx.lincheck.strategy.* -@StressCTest(iterations = 1, actorsBefore = 0, actorsAfter = 0, - requireStateEquivalenceImplCheck = false, minimizeFailedScenario = false) -class HangingTest { +class HangingTest : AbstractLincheckTest(DeadlockedWithDumpFailedIteration::class) { @Operation fun badOperation() { while (true) {} } - @Test(expected = AssertionError::class) - fun test() = LinChecker.check(this::class.java) + override fun > O.customize() { + iterations(1) + actorsBefore(0) + actorsAfter(0) + requireStateEquivalenceImplCheck(false) + minimizeFailedScenario(false) + } + } diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java similarity index 51% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java index 73e2a0000..5753661b9 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java @@ -22,28 +22,19 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.LoggingLevel; -import org.jetbrains.kotlinx.lincheck.annotations.LogLevel; -import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.annotations.Param; -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jctools.queues.atomic.SpscLinkedAtomicQueue; -import org.junit.Test; +import org.jctools.queues.atomic.*; +import org.jetbrains.annotations.*; +import org.jetbrains.kotlinx.lincheck.annotations.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.*; @OpGroupConfig(name = "producer", nonParallel = true) @OpGroupConfig(name = "consumer", nonParallel = true) -@StressCTest(requireStateEquivalenceImplCheck = false) -public class NonParallelOpGroupTest { - private SpscLinkedAtomicQueue queue = new SpscLinkedAtomicQueue<>(); - private AtomicInteger i = new AtomicInteger(); +public class NonParallelOpGroupTest extends AbstractLincheckTest { + private final SpscLinkedAtomicQueue queue = new SpscLinkedAtomicQueue<>(); @Operation(group = "producer") - public void offer(@Param(gen = IntGen.class) Integer x) { + public void offer(Integer x) { queue.offer(x); } @@ -52,13 +43,15 @@ public Integer poll() { return queue.poll(); } - @Operation - public int incAndGet() { - return i.incrementAndGet(); - } - - @Test - public void test() { - LinChecker.check(NonParallelOpGroupTest.class); + @NotNull + @Override + protected Object extractState() { + List elements = new ArrayList<>(); + while (true) { + Integer el = poll(); + if (el == null) break; + elements.add(el); + } + return elements; } } diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/OperationsInAbstractClassTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/OperationsInAbstractClassTest.kt similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/OperationsInAbstractClassTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/OperationsInAbstractClassTest.kt diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java similarity index 67% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java index f826f8916..3bf46b841 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java @@ -22,7 +22,8 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.LinChecker; +import org.jetbrains.annotations.*; +import org.jetbrains.kotlinx.lincheck.*; import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig; import org.jetbrains.kotlinx.lincheck.annotations.Operation; import org.jetbrains.kotlinx.lincheck.annotations.Param; @@ -35,7 +36,7 @@ import java.util.List; @OpGroupConfig(name = "push_remove", nonParallel = true) -@StressCTest +@StressCTest(iterations = 5, actorsBefore = 0, actorsPerThread = 2, actorsAfter = 0) public class ResultAsParameterTest extends VerifierState { private final Stack stack = new Stack(); private Node lastPushNode = null; @@ -63,6 +64,7 @@ public void test() { LinChecker.check(ResultAsParameterTest.class); } + @NotNull @Override protected Object extractState() { List elements = new ArrayList<>(); @@ -70,47 +72,47 @@ protected Object extractState() { while ((e = stack.pop()) != -1) elements.add(e); return elements; } -} - -class Stack { - private final Node NIL = new Node(null, 0); - private Node head = NIL; - synchronized Node push(int x) { // x >= 0 - Node node = new Node(head, x); - head.prev = node; - head = node; - return node; - } + private static class Stack { + private final Node NIL = new Node(null, 0); + private Node head = NIL; - synchronized int pop() { - if (head == NIL) { - return -1; + synchronized Node push(int x) { // x >= 0 + Node node = new Node(head, x); + head.prev = node; + head = node; + return node; } - int res = head.val; - head = head.next; - return res; - } - synchronized boolean remove(Node node) { - if (node == head) { + synchronized int pop() { + if (head == NIL) { + return -1; + } + int res = head.val; head = head.next; - return true; - } else if (node.prev != null && node.prev.next == node) { - node.prev.next = node.next; - return true; + return res; + } + + synchronized boolean remove(Node node) { + if (node == head) { + head = head.next; + return true; + } else if (node.prev != null && node.prev.next == node) { + node.prev.next = node.next; + return true; + } + return false; } - return false; } -} -class Node { - Node next; - Node prev; - int val; + private static class Node { + Node next; + Node prev; + int val; - public Node(Node next, int val) { - this.next = next; - this.val = val; + public Node(Node next, int val) { + this.next = next; + this.val = val; + } } } diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java similarity index 91% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java index 67f1addf8..e1a34333a 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java @@ -27,9 +27,9 @@ import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; import org.junit.Test; -@StressCTest(threads = 3, iterations = 100, invocationsPerIteration = 10, requireStateEquivalenceImplCheck = false) +@StressCTest(threads = 3, iterations = 10, invocationsPerIteration = 10, requireStateEquivalenceImplCheck = false) public class RunOnceTest { - private A a = new A();; + private A a = new A(); @Operation(runOnce = true) public void a() { @@ -60,4 +60,4 @@ synchronized void b() { b = true; } } -} +} \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt similarity index 95% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt index 308f32d91..8006ec16e 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/StateEquivalenceImplCheckTest.kt @@ -38,5 +38,5 @@ class StateEquivalenceImplCheckTest { // Check that IllegalStateException is thrown if `requireStateEquivalenceImplCheck` option is true by default // and hashCode/equals methods are not implemented @Test(expected = IllegalStateException::class) - fun test() = LinChecker.check(StateEquivalenceImplCheckTest::class.java) + fun test() = LinChecker.check(this::class.java) } diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt new file mode 100644 index 000000000..46f1d1136 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt @@ -0,0 +1,57 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test.runner + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.test.AbstractLincheckTest + +class DeadlockOnSynchronizedTest : AbstractLincheckTest(DeadlockedWithDumpFailedIteration::class) { + private var counter = 0 + private var lock1 = Any() + private var lock2 = Any() + + @Operation + fun inc12(): Int { + synchronized(lock1) { + synchronized(lock2) { + return counter++ + } + } + } + + @Operation + fun inc21(): Int { + synchronized(lock2) { + synchronized(lock1) { + return counter++ + } + } + } + + override fun > O.customize() { + minimizeFailedScenario(false) + } + + override fun extractState(): Any = counter +} diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt similarity index 85% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt index 172010a0d..b540a6412 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt @@ -21,12 +21,12 @@ */ package org.jetbrains.kotlinx.lincheck.test.runner -import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner import org.junit.Test import kotlin.coroutines.intrinsics.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.strategy.Strategy +import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.test.verifier.* import org.junit.Assert.* import java.util.concurrent.atomic.AtomicReference @@ -67,8 +67,7 @@ class SuspendResumeScenarios { } fun resumeWithException() { - while (continuation.get() == null) { - } + while (continuation.get() == null) {} continuation.get()!!.resumeWithException(TestException()) } @@ -85,12 +84,6 @@ class SuspendResumeScenarios { * Test [ParallelThreadsRunner] different suspend-resume scenarios with exceptions. */ class ParallelThreadsRunnerExceptionTest { - val mockStrategy = object : Strategy(null, null, null) { - override fun run() { - throw UnsupportedOperationException() - } - } - val testClass = SuspendResumeScenarios::class.java private val susWithoutException = SuspendResumeScenarios::suspendWithoutException @@ -114,8 +107,8 @@ class ParallelThreadsRunnerExceptionTest { } } } - val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null) - val results = runner.run() + val runner = ParallelThreadsRunner(mockStrategy(scenario), testClass, null) + val results = (runner.run() as CompletedInvocationResult).results assertTrue(results.equalsIgnoringClocks(expectedResults)) } @@ -133,8 +126,8 @@ class ParallelThreadsRunnerExceptionTest { } } } - val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null) - val results = runner.run() + val runner = ParallelThreadsRunner(mockStrategy(scenario), testClass, null) + val results = (runner.run() as CompletedInvocationResult).results assertTrue(results.equalsIgnoringClocks(expectedResults)) } @@ -147,8 +140,12 @@ class ParallelThreadsRunnerExceptionTest { } } } - val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null) - val results = runner.run() + val runner = ParallelThreadsRunner(mockStrategy(scenario), testClass, null) + val results = (runner.run() as CompletedInvocationResult).results assertTrue(results.equalsIgnoringClocks(expectedResults)) } +} + +fun mockStrategy(scenario: ExecutionScenario) = object : Strategy(scenario) { + override fun run(): FailedIteration? = error("Not yet implemented") } \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ResumingFollowUpTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ResumingFollowUpTest.kt similarity index 97% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ResumingFollowUpTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ResumingFollowUpTest.kt index f8433fd7a..daba908f2 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ResumingFollowUpTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ResumingFollowUpTest.kt @@ -19,7 +19,7 @@ * . * #L% */ -package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability +package org.jetbrains.kotlinx.lincheck.test.runner import org.jetbrains.kotlinx.lincheck.Suspended import org.jetbrains.kotlinx.lincheck.ValueResult diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java similarity index 56% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index b99f59aa4..75e3386f5 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -23,36 +23,31 @@ */ import org.jetbrains.kotlinx.lincheck.*; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult; -import org.jetbrains.kotlinx.lincheck.runner.Runner; -import org.jetbrains.kotlinx.lincheck.runner.TestThreadExecution; -import org.jetbrains.kotlinx.lincheck.runner.TestThreadExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.strategy.Strategy; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.runner.*; +import org.jetbrains.kotlinx.lincheck.strategy.*; +import org.junit.*; -import java.util.ArrayDeque; -import java.util.Collections; -import java.util.NoSuchElementException; -import java.util.Queue; +import java.util.*; -import static java.util.Arrays.asList; +import static java.util.Arrays.*; +import static java.util.Collections.*; public class TestThreadExecutionHelperTest { private Runner runner; @Before public void setUp() { - Strategy mockStrategy = new Strategy(null, null, null) { + ExecutionScenario scenario = new ExecutionScenario(emptyList(), emptyList(), emptyList()); + Strategy strategy = new Strategy(scenario) { @Override - public void run(){ + public FailedIteration run() { throw new UnsupportedOperationException(); } }; - runner = new Runner(null, mockStrategy, ArrayDeque.class) { + runner = new Runner(strategy, ArrayDeque.class) { @Override - public ExecutionResult run() { + public InvocationResult run() { throw new UnsupportedOperationException(); } }; @@ -62,16 +57,16 @@ public ExecutionResult run() { public void testBase() throws Exception { TestThreadExecution ex = TestThreadExecutionGenerator.create(runner, 0, asList( - new Actor(Queue.class.getMethod("add", Object.class), asList(1), Collections.emptyList()), - new Actor(Queue.class.getMethod("add", Object.class), asList(2), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("element"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("peek"), Collections.emptyList(), Collections.emptyList()) - ), Collections.emptyList(), false, false); + new Actor(Queue.class.getMethod("add", Object.class), asList(1), emptyList()), + new Actor(Queue.class.getMethod("add", Object.class), asList(2), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("element"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("peek"), emptyList(), emptyList()) + ), emptyList(), false, false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[5]; ex.run(); - Assert.assertArrayEquals(new Result[] { + Assert.assertArrayEquals(new Result[]{ new ValueResult(true), new ValueResult(true), new ValueResult(1), @@ -84,11 +79,11 @@ public void testBase() throws Exception { public void testGlobalException() throws Exception { TestThreadExecution ex = TestThreadExecutionGenerator.create(runner, 0, asList( - new Actor(Queue.class.getMethod("add", Object.class), asList(1), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("add", Object.class), asList(2), Collections.emptyList()) - ), Collections.emptyList(), false, false); + new Actor(Queue.class.getMethod("add", Object.class), asList(1), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("add", Object.class), asList(2), emptyList()) + ), emptyList(), false, false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[4]; ex.run(); @@ -98,17 +93,17 @@ public void testGlobalException() throws Exception { public void testActorExceptionHandling() throws Exception { TestThreadExecution ex = TestThreadExecutionGenerator.create(runner, 0, asList( - new Actor(ArrayDeque.class.getMethod("addLast", Object.class), asList(1), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), asList(NoSuchElementException.class)), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), asList(Exception.class, NoSuchElementException.class)) - ), Collections.emptyList(), false, false); + new Actor(ArrayDeque.class.getMethod("addLast", Object.class), asList(1), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(NoSuchElementException.class)), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(Exception.class, NoSuchElementException.class)) + ), emptyList(), false, false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[4]; ex.clocks = new int[4][1]; ex.allThreadExecutions = (TestThreadExecution[]) asList(ex).toArray(); ex.run(); - Assert.assertArrayEquals(new Result[] { + Assert.assertArrayEquals(new Result[]{ VoidResult.INSTANCE, new ValueResult(1), ExceptionResult.Companion.create(NoSuchElementException.class), @@ -120,15 +115,15 @@ public void testActorExceptionHandling() throws Exception { public void testWaits() throws Exception { TestThreadExecution ex = TestThreadExecutionGenerator.create(runner, 0, asList( - new Actor(Queue.class.getMethod("add", Object.class), asList(1), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), Collections.emptyList()), - new Actor(Queue.class.getMethod("remove"), Collections.emptyList(), asList(NoSuchElementException.class)) - ), Collections.emptyList(), true, false); + new Actor(Queue.class.getMethod("add", Object.class), asList(1), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), emptyList()), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(NoSuchElementException.class)) + ), emptyList(), true, false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[3]; - ex.waits = new int[] {15, 100}; + ex.waits = new int[]{15, 100}; ex.run(); - Assert.assertArrayEquals(new Result[] { + Assert.assertArrayEquals(new Result[]{ new ValueResult(true), new ValueResult(1), ExceptionResult.Companion.create(NoSuchElementException.class) diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java similarity index 69% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java index 72fb8a5a5..2a9e8bc37 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java @@ -22,20 +22,22 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.LinChecker; +import org.jetbrains.annotations.*; +import org.jetbrains.kotlinx.lincheck.*; import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; -import org.junit.Test; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.*; +import org.jetbrains.kotlinx.lincheck.strategy.stress.*; +import org.jetbrains.kotlinx.lincheck.verifier.*; +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*; +import org.junit.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; -@StressCTest(threads = 3, actorsPerThread = 3, iterations = 10, invocationsPerIteration = 5, +@RandomSwitchCTest(threads = 3, actorsPerThread = 3, iterations = 10, invocationsPerIteration = 5, generator = RandomExecutionGenerator.class, verifier = LinearizabilityVerifier.class) public class RandomSwitchCTestAnnTest extends VerifierState { - private AtomicInteger i = new AtomicInteger(); + private final AtomicInteger i = new AtomicInteger(); @Operation() public int incAndGet() { @@ -47,6 +49,7 @@ public void test() { LinChecker.check(RandomSwitchCTestAnnTest.class); } + @NotNull @Override protected Object extractState() { return i.get(); diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java similarity index 71% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java index 980d87f71..304315c99 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java @@ -22,19 +22,17 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.Options; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchOptions; +import org.jetbrains.kotlinx.lincheck.*; +import org.jetbrains.kotlinx.lincheck.annotations.*; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.*; +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*; +import org.junit.*; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; -import org.junit.Test; - -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; public class RandomSwitchOptionsTest { - private AtomicInteger i = new AtomicInteger(); + private final AtomicInteger i = new AtomicInteger(); @Operation() public int incAndGet() { @@ -43,7 +41,7 @@ public int incAndGet() { @Test public void test() { - Options opts = new RandomSwitchOptions() + RandomSwitchOptions opts = new RandomSwitchOptions() .iterations(10) .invocationsPerIteration(200) .executionGenerator(RandomExecutionGenerator.class) diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java similarity index 73% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java index 80f545b17..8a7f9b1fd 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java @@ -22,21 +22,21 @@ * #L% */ -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlinx.lincheck.LinChecker; +import org.jetbrains.annotations.*; +import org.jetbrains.kotlinx.lincheck.*; import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest; -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; -import org.junit.Test; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.stress.*; +import org.jetbrains.kotlinx.lincheck.verifier.*; +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*; +import org.junit.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; @StressCTest(threads = 3, actorsPerThread = 3, iterations = 10, invocationsPerIteration = 5, generator = RandomExecutionGenerator.class, verifier = LinearizabilityVerifier.class) public class StressCTestAnnTest extends VerifierState { - private AtomicInteger i = new AtomicInteger(); + private final AtomicInteger i = new AtomicInteger(); @Operation() public int incAndGet() { @@ -48,6 +48,7 @@ public void test() { LinChecker.check(StressCTestAnnTest.class); } + @NotNull @Override protected Object extractState() { return i.get(); diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java similarity index 67% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java index edbb6bccb..545276e12 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.test.strategy.stress; - /* * #%L * Lincheck @@ -10,32 +8,31 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program 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 General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.test.strategy.stress; -import org.jetbrains.kotlinx.lincheck.LinChecker; -import org.jetbrains.kotlinx.lincheck.LoggingLevel; -import org.jetbrains.kotlinx.lincheck.Options; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; -import org.junit.Test; +import org.jetbrains.kotlinx.lincheck.*; +import org.jetbrains.kotlinx.lincheck.annotations.*; +import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.strategy.stress.*; +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*; +import org.junit.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; public class StressOptionsTest { - private AtomicInteger i = new AtomicInteger(); + private final AtomicInteger i = new AtomicInteger(); @Operation() public int incAndGet() { @@ -44,14 +41,14 @@ public int incAndGet() { @Test public void test() { - Options opts = new StressOptions() + StressOptions opts = new StressOptions() .iterations(10) .invocationsPerIteration(200) .executionGenerator(RandomExecutionGenerator.class) .verifier(LinearizabilityVerifier.class) .threads(2) .actorsPerThread(3) - .logLevel(LoggingLevel.INFO) + .logLevel(LoggingLevel.ERROR) .requireStateEquivalenceImplCheck(false) .minimizeFailedScenario(false); LinChecker.check(StressOptionsTest.class, opts); diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt similarity index 78% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt index 19593cf59..ed9befc22 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.test.verifier - /* * #%L * Lincheck @@ -21,15 +19,15 @@ package org.jetbrains.kotlinx.lincheck.test.verifier * . * #L% */ +package org.jetbrains.kotlinx.lincheck.test.verifier -import org.jetbrains.kotlinx.lincheck.LinChecker +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest -import org.jetbrains.kotlinx.lincheck.verifier.EpsilonVerifier -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* -@StressCTest(verifier = EpsilonVerifier::class) +@StressCTest(iterations = 5, threads = 2, actorsPerThread = 2, verifier = EpsilonVerifier::class) class EpsilonVerifierTest : VerifierState() { private var i = 0 diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt similarity index 68% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt index d8511fd57..bc92ac0fe 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt @@ -21,24 +21,26 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier -import org.jetbrains.kotlinx.lincheck.LinChecker +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.util.concurrent.atomic.AtomicInteger +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.test.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import java.util.concurrent.atomic.* -@StressCTest(sequentialSpecification = CorrectCounter::class) -class IncorrectCounter { - private var c = AtomicInteger() +class SequentialSpecificationTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class) { + private val c = AtomicInteger() @Operation fun set(value: Int) = c.set(value) + @Operation fun get() = c.get() + 1 - @Test(expected = AssertionError::class) - fun test() = LinChecker.check(this::class.java) + override fun > O.customize() { + sequentialSpecification(CorrectCounter::class.java) + } } class CorrectCounter: VerifierState() { diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt similarity index 94% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt index 553c21531..491d40a1e 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelCustomTest.kt @@ -21,13 +21,13 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability +import kotlinx.coroutines.channels.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.test.verifier.* -import kotlinx.coroutines.channels.Channel -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.util.concurrent.CancellationException +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* +import org.junit.* +import java.util.concurrent.* class BufferedChannelCustomTest : VerifierState() { private val ch = Channel(3) diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelTest.kt similarity index 83% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelTest.kt index 552cce0aa..c5daa7a78 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelTest.kt @@ -21,16 +21,16 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability +import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.junit.* +import org.jetbrains.kotlinx.lincheck.test.* +@InternalCoroutinesApi @Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(actorsPerThread = 3, sequentialSpecification = SequentiaBuffered2IntChannel::class) -class BufferedChannelStressTest { +class BufferedChannelTest : AbstractLincheckTest() { private val c = Channel(2) @Operation(cancellableOnSuspension = false) @@ -45,8 +45,10 @@ class BufferedChannelStressTest { @Operation fun offer(@Param(name = "value") value: Int) = c.offer(value) - @Test - fun test() = LinChecker.check(BufferedChannelStressTest::class.java) + override fun > O.customize() { + sequentialSpecification(SequentiaBuffered2IntChannel::class.java) + } } +@InternalCoroutinesApi class SequentiaBuffered2IntChannel : SequentialIntChannel(capacity = 2) \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt similarity index 96% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt index e4ff8b2dc..c0f6710c8 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt @@ -62,8 +62,8 @@ class ClocksTest { LinChecker.check(this::class.java) throw AssertionError("Should fail since the execution with clocks is " + "sequential consistent but not linearizable") - } catch (e: AssertionError) { - check(e.message!!.contains("Invalid interleaving found")) { + } catch (e: LincheckAssertionError) { + assert(e.message!!.contains("Invalid execution results")) { "Should fail because of the invalid interleaving, but the following exception was thrown:\n" + e.message } } diff --git a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/ConcurrentDequeStressTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt similarity index 61% rename from libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/ConcurrentDequeStressTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt index 921179189..70f0b7ff5 100644 --- a/libtest/src/test/java/org/jetbrains/kotlinx/lincheck/tests/linearizability/ConcurrentDequeStressTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt @@ -19,22 +19,17 @@ * . * #L% */ -package org.jetbrains.kotlinx.lincheck.tests.linearizability +package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability -import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* -import org.jetbrains.kotlinx.lincheck.paramgen.IntGen -import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.lang.AssertionError -import java.util.concurrent.ConcurrentLinkedDeque +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.test.* +import java.util.concurrent.* @Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(actorsPerThread = 10, threads = 2, invocationsPerIteration = 10000, iterations = 10, actorsBefore = 10, actorsAfter = 10) -class ConcurrentDequeStressTest : VerifierState() { - - val deque = ConcurrentLinkedDeque() +class ConcurrentDequeTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class) { + private val deque = ConcurrentLinkedDeque() @Operation fun addFirst(e: Int) = deque.addFirst(e) @@ -48,8 +43,5 @@ class ConcurrentDequeStressTest : VerifierState() { @Operation fun pollLast() = deque.pollLast() - @Test(expected = AssertionError::class) - fun test() = LinChecker.check(ConcurrentDequeStressTest::class.java) - override fun extractState() = deque.toList() } diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java similarity index 67% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java index d3ce3771f..6eb271114 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentHashMapTest.java @@ -22,20 +22,16 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.*; +import org.jetbrains.annotations.*; import org.jetbrains.kotlinx.lincheck.annotations.*; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; import org.jetbrains.kotlinx.lincheck.paramgen.*; -import org.jetbrains.kotlinx.lincheck.strategy.stress.*; -import org.jetbrains.kotlinx.lincheck.verifier.*; -import org.junit.Test; +import org.jetbrains.kotlinx.lincheck.test.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.*; @Param(name = "key", gen = IntGen.class, conf = "1:5") -@StressCTest(actorsPerThread = 10, threads = 2, invocationsPerIteration = 1000, iterations = 100, actorsBefore = 10, actorsAfter = 10) -public class ConcurrentHashMapTest extends VerifierState { - private ConcurrentHashMap map = new ConcurrentHashMap<>(); +public class ConcurrentHashMapTest extends AbstractLincheckTest { + private final ConcurrentHashMap map = new ConcurrentHashMap<>(); @Operation public Integer put(@Param(name = "key") Integer key, Integer value) { @@ -47,9 +43,7 @@ public Integer get(@Param(name = "key") Integer key) { return map.get(key); } - @Test - public void test() { LinChecker.check(ConcurrentHashMapTest.class); } - + @NotNull @Override protected Object extractState() { return map; diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentQueueStressTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentLinkedQueueTest.kt similarity index 67% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentQueueStressTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentLinkedQueueTest.kt index da04efd29..f2490f157 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentQueueStressTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentLinkedQueueTest.kt @@ -21,18 +21,13 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability -import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.util.concurrent.ConcurrentLinkedQueue +import org.jetbrains.kotlinx.lincheck.test.* +import java.util.concurrent.* @Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(verifier = LinearizabilityVerifier::class, iterations = 10, invocationsPerIteration = 1000, actorsBefore = 10, actorsAfter = 10, actorsPerThread = 5, threads = 3) -class ConcurrentQueueStressTest : VerifierState() { +class ConcurrentLinkedQueueTest : AbstractLincheckTest() { private val queue = ConcurrentLinkedQueue() @Operation @@ -47,8 +42,5 @@ class ConcurrentQueueStressTest : VerifierState() { @Operation fun poll() = queue.poll() - @Test - fun test() = LinChecker.check(ConcurrentQueueStressTest::class.java) - override fun extractState() = queue.toList() } diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt new file mode 100644 index 000000000..ea82b155d --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt @@ -0,0 +1,80 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability + +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.test.AbstractLincheckTest +import java.util.concurrent.atomic.* +import kotlin.reflect.KClass + +abstract class AbstractCounterTest( + private val counter: Counter, + vararg expectedErrors: KClass +) : AbstractLincheckTest(*expectedErrors) { + @Operation + fun incAndGet(): Int = counter.incAndGet() + + override fun extractState(): Any = counter.get() +} + +class CounterCorrectTest : AbstractCounterTest(CounterCorrect()) +class CounterWrong0Test : AbstractCounterTest(CounterWrong0(), IncorrectResultsFailedIteration::class) +class CounterWrong1Test : AbstractCounterTest(CounterWrong1(), IncorrectResultsFailedIteration::class) +class CounterWrong2Test : AbstractCounterTest(CounterWrong2(), IncorrectResultsFailedIteration::class) + +interface Counter { + fun incAndGet(): Int + fun get(): Int +} + +private class CounterWrong0 : Counter { + private var c: Int = 0 + + override fun incAndGet(): Int = ++c + override fun get(): Int = c +} + +private class CounterWrong1 : Counter { + private var c: Int = 0 + + override fun incAndGet(): Int { + c++ + return c + } + override fun get(): Int = c +} + +private class CounterWrong2 : Counter { + @Volatile + private var c: Int = 0 + + override fun incAndGet(): Int = ++c + override fun get(): Int = c +} + +private class CounterCorrect : Counter { + private val c = AtomicInteger() + + override fun incAndGet(): Int = c.incrementAndGet() + override fun get(): Int = c.get() +} diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt new file mode 100644 index 000000000..a3f806203 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt @@ -0,0 +1,42 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability + +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.test.* +import java.util.* + +@Param(name = "key", gen = IntGen::class) +class HashMapTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class, UnexpectedExceptionFailedIteration::class) { + private val m = HashMap() + + @Operation + fun put(key: Int, @Param(name = "key") value: Int): Int? = m.put(key, value) + + @Operation + operator fun get(@Param(name = "key") key: Int?): Int? = m[key] + + override fun extractState(): Any = m +} + diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/LockBasedSetTests.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/LockBasedSetTests.kt new file mode 100644 index 000000000..e5552fc93 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/LockBasedSetTests.kt @@ -0,0 +1,126 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability + +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.test.* +import java.util.concurrent.atomic.* +import java.util.concurrent.locks.* +import kotlin.concurrent.* + +@Param(name = "key", gen = IntGen::class, conf = "1:5") +abstract class AbstractSetTest(private val set: Set) : AbstractLincheckTest() { + @Operation + fun add(@Param(name = "key") key: Int): Boolean = set.add(key) + + @Operation + fun remove(@Param(name = "key") key: Int): Boolean = set.remove(key) + + @Operation + operator fun contains(@Param(name = "key") key: Int): Boolean = set.contains(key) + + override fun extractState(): Any = (1..5).map { set.contains(it) } +} + +class SpinLockSetTest : AbstractSetTest(SpinLockBasedSet()) +class ReentrantLockSetTest : AbstractSetTest(ReentrantLockBasedSet()) +class SynchronizedLockSetTest : AbstractSetTest(SynchronizedBlockBasedSet()) +class SynchronizedMethodSetTest : AbstractSetTest(SynchronizedMethodBasedSet()) + +interface Set { + fun add(key: Int): Boolean + fun remove(key: Int): Boolean + fun contains(key: Int): Boolean +} + +internal class SpinLockBasedSet : Set { + private val set = mutableSetOf() + private val locked = AtomicBoolean() + + override fun add(key: Int): Boolean = withSpinLock { + set.add(key) + } + + override fun remove(key: Int): Boolean = withSpinLock { + set.remove(key) + } + + override fun contains(key: Int): Boolean = withSpinLock { + set.contains(key) + } + + private fun withSpinLock(block: () -> Boolean): Boolean { + while (!locked.compareAndSet(false, true)) { Thread.yield() } + try { + return block() + } finally { + locked.set(false) + } + } +} + +internal class ReentrantLockBasedSet : Set { + private val set = mutableSetOf() + private val lock = ReentrantLock() + + override fun add(key: Int): Boolean = lock.withLock { + set.add(key) + } + + override fun remove(key: Int): Boolean = lock.withLock { + set.remove(key) + } + + override fun contains(key: Int): Boolean = lock.withLock { + set.contains(key) + } +} + +internal class SynchronizedBlockBasedSet : Set { + private val set = mutableSetOf() + + override fun add(key: Int): Boolean = synchronized(set) { + set.add(key) + } + + override fun remove(key: Int): Boolean = synchronized(set) { + set.remove(key) + } + + override fun contains(key: Int): Boolean = synchronized(set) { + set.contains(key) + } +} + +internal class SynchronizedMethodBasedSet : Set { + private val set = mutableSetOf() + + @Synchronized + override fun add(key: Int): Boolean = set.add(key) + + @Synchronized + override fun remove(key: Int): Boolean = set.remove(key) + + @Synchronized + override fun contains(key: Int): Boolean = set.contains(key) +} \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelCustomTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelCustomTest.kt similarity index 100% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelCustomTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelCustomTest.kt diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelTest.kt similarity index 74% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelTest.kt index 76ab40111..9dd2e0a6e 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelTest.kt @@ -21,34 +21,35 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability +import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* -import org.junit.* +import org.jetbrains.kotlinx.lincheck.test.* -@LogLevel(LoggingLevel.DEBUG) +@ExperimentalCoroutinesApi +@InternalCoroutinesApi @Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(sequentialSpecification = SequentialRendezvousIntChannel::class, verifier = LinearizabilityVerifier::class) -class RendezvousChannelStressTest { +class RendezvousChannelTest : AbstractLincheckTest() { private val ch = Channel() @Operation(handleExceptionsAsResult = [ClosedSendChannelException::class]) suspend fun send(@Param(name = "value") value: Int) = ch.send(value) @Operation(handleExceptionsAsResult = [ClosedReceiveChannelException::class]) - suspend fun receive() = ch.receive() + 100 + suspend fun receive() = ch.receive() @Operation(handleExceptionsAsResult = [ClosedReceiveChannelException::class]) - suspend fun receiveOrNull() = ch.receiveOrNull()?.plus(100) + suspend fun receiveOrNull() = ch.receiveOrNull() @Operation fun close() = ch.close() - @Test - fun test() = LinChecker.check(RendezvousChannelStressTest::class.java) + override fun > O.customize() { + sequentialSpecification(SequentialRendezvousIntChannel::class.java) + } } +@InternalCoroutinesApi class SequentialRendezvousIntChannel : SequentialIntChannel(capacity = 0) \ No newline at end of file diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt new file mode 100644 index 000000000..fe8fdbbf3 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt @@ -0,0 +1,118 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import org.jetbrains.kotlinx.lincheck.verifier.* + +@InternalCoroutinesApi +open class SequentialIntChannel(private val capacity: Int) : VerifierState() { + private val senders = ArrayList, Int>>() + private val receivers = ArrayList>() + private val buffer = ArrayList() + private var closed = false + + suspend fun send(x: Int) { + if (offer(x)) return + suspendAtomicCancellableCoroutine { cont -> + senders.add(cont to x) + } + } + + fun offer(x: Int): Boolean { + while (true) { + if (closed) throw ClosedSendChannelException("") + if (receivers.isNotEmpty()) { + val r = receivers.removeAt(0) + if (r.tryResume0(x)) return true + } else { + if (buffer.size == capacity) return false + buffer.add(x) + return true + } + } + } + + suspend fun receive(): Int { + val res = receiveImpl() + if (res === CLOSED) throw ClosedReceiveChannelException("") + return res as Int + } + + @InternalCoroutinesApi + suspend fun receiveOrNull(): Int? { + val res = receiveImpl() + return if (res === CLOSED) null + else res as Int + } + + suspend fun receiveImpl(): Any { + if (buffer.isEmpty() && senders.isEmpty() && closed) return CLOSED + val pollResult = poll() + if (pollResult !== null) return pollResult + return suspendAtomicCancellableCoroutine { cont -> + receivers.add(cont) + } + } + + fun poll(): Int? { + if (buffer.size > 0) { + val res = buffer.removeAt(0) + while (true) { + if (senders.isEmpty()) break + val (s, x) = senders.removeAt(0) + if (s.tryResume0(Unit)) { + buffer.add(x) + break + } + } + return res + } + while (true) { + if (senders.isEmpty()) return null + val (s, x) = senders.removeAt(0) + if (s.tryResume0(Unit)) return x + } + } + + fun close(): Boolean { + if (closed) return false + closed = true + receivers.forEach { + it.tryResume0(CLOSED) + } + receivers.clear() + return true + } + + override fun extractState() = buffer to closed +} + +@InternalCoroutinesApi +private fun CancellableContinuation.tryResume0(res: T): Boolean { + val token = tryResume(res) ?: return false + completeResume(token) + return true +} + +private val CLOSED = Any() \ No newline at end of file diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentSkipListMapStressTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt similarity index 63% rename from lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentSkipListMapStressTest.kt rename to src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt index d5563a5f6..696604ce3 100644 --- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentSkipListMapStressTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt @@ -24,17 +24,13 @@ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier -import org.jetbrains.kotlinx.lincheck.verifier.VerifierState -import org.junit.Test -import java.util.concurrent.ConcurrentSkipListMap +import org.jetbrains.kotlinx.lincheck.test.* +import org.junit.* +import java.util.concurrent.* @Param(name = "value", gen = IntGen::class, conf = "1:5") -@StressCTest(verifier = LinearizabilityVerifier::class, iterations = 10, invocationsPerIteration = 1000, actorsBefore = 10, actorsAfter = 10, actorsPerThread = 5, threads = 3) -class ConcurrentSkipListMapStressTest : VerifierState() { - - val skiplistMap = ConcurrentSkipListMap() +class SkipListMapTest : AbstractLincheckTest() { + private val skiplistMap = ConcurrentSkipListMap() @Operation fun put(key: Int, value: Int) = skiplistMap.put(key, value) @@ -48,14 +44,5 @@ class ConcurrentSkipListMapStressTest : VerifierState() { @Operation fun remove(key: Int) = skiplistMap.remove(key) - @Operation - fun ceilingEntry(key: Int) = skiplistMap.ceilingEntry(key) - - @Operation - fun ceilingKey(key: Int) = skiplistMap.ceilingKey(key) - - @Test - fun test() = LinChecker.check(ConcurrentSkipListMapStressTest::class.java) - override fun extractState() = skiplistMap.toMap() } diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeTaskQueueTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeTaskQueueTest.kt new file mode 100644 index 000000000..a2ccffa64 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeTaskQueueTest.kt @@ -0,0 +1,57 @@ +package org.jetbrains.kotlinx.lincheck.test.verifier.quiescent + +/* + * #%L + * Lincheck + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import kotlinx.coroutines.internal.* +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.test.* +import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* + +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "SubscriberImplementation") +@OpGroupConfig(name = "consumer", nonParallel = true) +@Param(name = "value", gen = IntGen::class, conf = "1:3") +class LockFreeTaskQueueTest: AbstractLincheckTest() { + private val q = LockFreeTaskQueue(true) + + @Operation + fun addLast(@Param(name = "value") value: Int) = q.addLast(value) + + @QuiescentConsistent + @Operation(group = "consumer") + fun removeFirstOrNull() = q.removeFirstOrNull() + + @Operation + fun close() = q.close() + + override fun > O.customize() { + actorsBefore(0) + actorsAfter(0) + threads(2) + actorsPerThread(3) + verifier(QuiescentConsistencyVerifier::class.java) + } + + override fun extractState() = q.map { it } to q.isClosed() +} \ No newline at end of file diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueTest.kt new file mode 100644 index 000000000..aada107d6 --- /dev/null +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueTest.kt @@ -0,0 +1,70 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +package org.jetbrains.kotlinx.lincheck.test.verifier.serializability + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.test.* +import org.jetbrains.kotlinx.lincheck.verifier.* + + +class SerializableQueueTest : AbstractLincheckTest() { + private val q = SerializableIntQueue() + + @Operation + fun put(x: Int): Unit = q.put(x) + + @Operation + fun poll(): Int? = q.poll() + + override fun > O.customize() { + actorsBefore(0) + actorsAfter(0) + actorsPerThread(2) + verifier(SerializabilityVerifier::class.java) + sequentialSpecification(SequentialIntQueue::class.java) + } +} + +data class SequentialIntQueue( + private val elements: MutableList = ArrayList() +) { + fun put(x: Int) { + elements += x + } + + fun poll(): Int? = if (elements.isEmpty()) null else elements.removeAt(0) +} + +data class SerializableIntQueue( + private val elements: MutableList = ArrayList() +) { + fun put(x: Int) = synchronized(this) { + elements += x + } + + fun poll(): Int? = synchronized(this) { + elements.shuffle() + return if (elements.isEmpty()) null else elements.removeAt(0) + } +} From c9051f0b0b91c51f1aec5e95e711dcb38d3a4209 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 13:15:17 +0300 Subject: [PATCH 2/6] Review fixes --- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 24 ++++----- .../lincheck/LincheckAssertionError.kt | 7 +-- .../jetbrains/kotlinx/lincheck/Reporter.kt | 40 +++++++------- .../lincheck/annotations/Operation.java | 4 +- .../lincheck/runner/InvocationResult.kt | 6 +-- ...{FailedIteration.kt => LincheckFailure.kt} | 22 ++++---- .../lincheck/strategy/ManagedStrategy.java | 4 +- .../kotlinx/lincheck/strategy/Strategy.kt | 6 +-- .../randomswitch/RandomSwitchStrategy.java | 10 ++-- .../strategy/stress/StressStrategy.kt | 6 +-- .../lincheck/verifier/CachedVerifier.java | 2 +- .../quiescent/QuiescentConsistencyVerifier.kt | 54 +++++++++++++------ .../lincheck/test/AbstractLincheckTest.kt | 10 ++-- .../kotlinx/lincheck/test/HangingTest.kt | 2 +- .../test/runner/DeadlockOnSynchronizedTest.kt | 2 +- .../ParallelThreadsRunnerEasyExceptionTest.kt | 2 +- .../runner/TestThreadExecutionHelperTest.java | 2 +- .../verifier/SequentialSpecificationTest.kt | 3 +- .../linearizability/ConcurrentDequeTest.kt | 2 +- .../verifier/linearizability/CounterTests.kt | 8 +-- .../verifier/linearizability/HashMapTest.kt | 2 +- 21 files changed, 117 insertions(+), 101 deletions(-) rename src/main/java/org/jetbrains/kotlinx/lincheck/strategy/{FailedIteration.kt => LincheckFailure.kt} (72%) diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt index f0be110ab..5ccb7416f 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -46,33 +46,33 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { * @throws LincheckAssertionError if the testing data structure is incorrect. */ fun check() { - val failedIteration = checkImpl() ?: return - throw LincheckAssertionError(failedIteration) + val failure = checkImpl() ?: return + throw LincheckAssertionError(failure) } /** * @return TestReport with information about concurrent test run. */ - internal fun checkImpl(): FailedIteration? { + internal fun checkImpl(): LincheckFailure? { check(testConfigurations.isNotEmpty()) { "No Lincheck test configuration to run" } for (testCfg in testConfigurations) { - val failedIteration = testCfg.checkImpl() - if (failedIteration != null) return failedIteration + val failure = testCfg.checkImpl() + if (failure != null) return failure } return null } - private fun CTestConfiguration.checkImpl(): FailedIteration? { + private fun CTestConfiguration.checkImpl(): LincheckFailure? { val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { iteration -> val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(iteration, iterations, scenario) - val failedIteration = scenario.run(this, verifier) - if (failedIteration != null) { - val minimizedFailedIteration = if (!minimizeFailedScenario) failedIteration - else failedIteration.minimize(this, verifier) + val failure = scenario.run(this, verifier) + if (failure != null) { + val minimizedFailedIteration = if (!minimizeFailedScenario) failure + else failure.minimize(this, verifier) reporter.logFailedIteration(minimizedFailedIteration) return minimizedFailedIteration } @@ -86,7 +86,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { // then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. // Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. // Thus, the algorithm works in the linear time of the total number of actors. - private fun FailedIteration.minimize(testCfg: CTestConfiguration, verifier: Verifier): FailedIteration { + private fun LincheckFailure.minimize(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure { reporter.logScenarioMinimization(scenario) for (i in scenario.parallelExecution.indices) { for (j in scenario.parallelExecution[i].indices) { @@ -115,7 +115,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun ExecutionScenario.tryMinimize(testCfg: CTestConfiguration, verifier: Verifier) = if (isValid) run(testCfg, verifier) else null - private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): FailedIteration? { + private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure? { val strategy = testCfg.createStrategy(testClass, this, verifier) return strategy.run() } diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt index 26fa98c39..866169cc9 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt @@ -23,10 +23,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.strategy.* import java.lang.AssertionError -import java.lang.StringBuilder class LincheckAssertionError( - failedIteration: FailedIteration -) : AssertionError(failedIteration.createMessage()) - -private fun FailedIteration.createMessage() = StringBuilder().appendFailedIteration(this).toString() \ No newline at end of file + failure: LincheckFailure +) : AssertionError(failure) \ No newline at end of file diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt index 9fe142a23..f52d9123f 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -36,8 +36,8 @@ class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: Pr } } - fun logFailedIteration(failedIteration: FailedIteration) = log(INFO) { - StringBuilder().appendFailedIteration(failedIteration) + fun logFailedIteration(failure: LincheckFailure) = log(INFO) { + StringBuilder().appendFailure(failure) } fun logScenarioMinimization(scenario: ExecutionScenario) = log(INFO) { @@ -135,26 +135,26 @@ private fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario) { } } -internal fun StringBuilder.appendFailedIteration(failedIteration: FailedIteration): StringBuilder = - when (failedIteration) { - is IncorrectResultsFailedIteration -> appendIncorrectResultsFailedIteration(failedIteration) - is DeadlockedWithDumpFailedIteration -> appendDeadlockedWithDumpFailedIteration(failedIteration) - is UnexpectedExceptionFailedIteration -> appendUnexpectedException(failedIteration) +internal fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder = + when (failure) { + is IncorrectResultsFailure -> appendIncorrectResultsFailure(failure) + is DeadlockWithDumpFailure -> appendDeadlockWithDumpFailure(failure) + is UnexpectedExceptionFailure -> appendUnexpectedExceptionFailure(failure) } -private fun StringBuilder.appendUnexpectedException(failedIteration: UnexpectedExceptionFailedIteration): StringBuilder { +private fun StringBuilder.appendUnexpectedExceptionFailure(failure: UnexpectedExceptionFailure): StringBuilder { appendln("= The execution failed with an unexpected exception =") - appendExecutionScenario(failedIteration.scenario) + appendExecutionScenario(failure.scenario) val sw = StringWriter() - failedIteration.exception.printStackTrace(PrintWriter(sw)) + failure.exception.printStackTrace(PrintWriter(sw)) appendln(sw.toString()) return this } -private fun StringBuilder.appendDeadlockedWithDumpFailedIteration(failedIteration: DeadlockedWithDumpFailedIteration): StringBuilder { +private fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { appendln("= The execution has hung, see the thread dump =") - appendExecutionScenario(failedIteration.scenario) - for ((t, stackTrace) in failedIteration.threadDump) { + appendExecutionScenario(failure.scenario) + for ((t, stackTrace) in failure.threadDump) { val threadNumber = if (t is ParallelThreadsRunner.TestThread) t.iThread else "?" appendln("Thread-$threadNumber:") for (ste in stackTrace) { @@ -165,21 +165,21 @@ private fun StringBuilder.appendDeadlockedWithDumpFailedIteration(failedIteratio return this } -private fun StringBuilder.appendIncorrectResultsFailedIteration(failedIteration: IncorrectResultsFailedIteration): StringBuilder { +private fun StringBuilder.appendIncorrectResultsFailure(failure: IncorrectResultsFailure): StringBuilder { appendln("= Invalid execution results =") - if (failedIteration.scenario.initExecution.isNotEmpty()) { + if (failure.scenario.initExecution.isNotEmpty()) { appendln("Init part:") - appendln(uniteActorsAndResultsLinear(failedIteration.scenario.initExecution, failedIteration.results.initResults)) + appendln(uniteActorsAndResultsLinear(failure.scenario.initExecution, failure.results.initResults)) } appendln("Parallel part:") - val parallelExecutionData = uniteParallelActorsAndResults(failedIteration.scenario.parallelExecution, failedIteration.results.parallelResultsWithClock) + val parallelExecutionData = uniteParallelActorsAndResults(failure.scenario.parallelExecution, failure.results.parallelResultsWithClock) append(printInColumns(parallelExecutionData)) - if (failedIteration.scenario.postExecution.isNotEmpty()) { + if (failure.scenario.postExecution.isNotEmpty()) { appendln() appendln("Post part:") - append(uniteActorsAndResultsLinear(failedIteration.scenario.postExecution, failedIteration.results.postResults)) + append(uniteActorsAndResultsLinear(failure.scenario.postExecution, failure.results.postResults)) } - if (failedIteration.results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) + if (failure.results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) appendln("\n---\nvalues in \"[..]\" brackets indicate the number of completed operations \n" + "in each of the parallel threads seen at the beginning of the current operation\n---") return this diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java index 19eac8359..997532b65 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/annotations/Operation.java @@ -33,8 +33,8 @@ @Target(ElementType.METHOD) public @interface Operation { /** - * Binds the arguments of this operations with the specified {@link Param parameter configurations} - * by theirs {@link Param#name()} names. + * Binds the arguments of this operation with the specified {@link Param parameter configurations} + * by their {@link Param#name()} names. */ String[] params() default {}; diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt index 7605a8b1a..ed940419e 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -31,14 +31,14 @@ sealed class InvocationResult /** * The invocation completed successfully, the output [results] are provided. */ -data class CompletedInvocationResult( +class CompletedInvocationResult( val results: ExecutionResult ) : InvocationResult() /** - * Indicates that the invocation has get into deadlock or livelock. + * Indicates that the invocation has run into deadlock or livelock. */ -data class DeadlockInvocationResult( +class DeadlockInvocationResult( val threadDump: Map> ) : InvocationResult() diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt similarity index 72% rename from src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt rename to src/main/java/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt index 7b6529693..ec7432095 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/FailedIteration.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt @@ -25,29 +25,29 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* -sealed class FailedIteration( +sealed class LincheckFailure( val scenario: ExecutionScenario ) { - override fun toString() = StringBuilder().appendFailedIteration(this).toString() + override fun toString() = StringBuilder().appendFailure(this).toString() } -internal class IncorrectResultsFailedIteration( +internal class IncorrectResultsFailure( scenario: ExecutionScenario, val results: ExecutionResult -) : FailedIteration(scenario) +) : LincheckFailure(scenario) -internal class DeadlockedWithDumpFailedIteration( +internal class DeadlockWithDumpFailure( scenario: ExecutionScenario, val threadDump: Map> -) : FailedIteration(scenario) +) : LincheckFailure(scenario) -internal class UnexpectedExceptionFailedIteration( +internal class UnexpectedExceptionFailure( scenario: ExecutionScenario, val exception: Throwable -) : FailedIteration(scenario) +) : LincheckFailure(scenario) -internal fun InvocationResult.toFailedIteration(scenario: ExecutionScenario) = when (this) { - is DeadlockInvocationResult -> DeadlockedWithDumpFailedIteration(scenario, threadDump) - is UnexpectedExceptionInvocationResult -> UnexpectedExceptionFailedIteration(scenario, exception) +internal fun InvocationResult.toLincheckFailure(scenario: ExecutionScenario) = when (this) { + is DeadlockInvocationResult -> DeadlockWithDumpFailure(scenario, threadDump) + is UnexpectedExceptionInvocationResult -> UnexpectedExceptionFailure(scenario, exception) else -> error("Unexpected invocation result type: ${this.javaClass.simpleName}") } \ No newline at end of file diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java index b282b41d0..d2db1844f 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java @@ -71,7 +71,7 @@ public boolean needsTransformation() { } @Override - public final FailedIteration run() { + public final LincheckFailure run() { try { return runImpl(); } finally { @@ -100,7 +100,7 @@ protected final StackTraceElement getLocationDescription(int codeLocation) { /** * This method implements the strategy logic */ - protected abstract FailedIteration runImpl(); + protected abstract LincheckFailure runImpl(); // == LISTENING EVENTS == diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt index 2ce431fd3..20754dd35 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -27,8 +27,8 @@ import org.objectweb.asm.* /** * Implementation of this class describes how to run the generated execution. * - * Note that strategy could run execution several times. For strategy creating - * [.createStrategy] method is used. It is impossible to add new strategy + * Note that strategy can run execution several times. For strategy creating + * [.createStrategy] method is used. It is impossible to add a new strategy * without any code change. */ abstract class Strategy protected constructor( @@ -39,5 +39,5 @@ abstract class Strategy protected constructor( throw UnsupportedOperationException("$javaClass strategy does not transform classes") } - abstract fun run(): FailedIteration? + abstract fun run(): LincheckFailure? } diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java index 0a7602b7f..b99e15f66 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java @@ -27,11 +27,11 @@ import org.jetbrains.kotlinx.lincheck.strategy.*; import org.jetbrains.kotlinx.lincheck.verifier.*; -import static org.jetbrains.kotlinx.lincheck.strategy.FailedIterationKt.toFailedIteration; +import static org.jetbrains.kotlinx.lincheck.strategy.LincheckFailureKt.toLincheckFailure; /** - * This managed strategy switches current thread to a random one with the specified probability. + * This managed strategy switches the current thread to a random one with the specified probability. * In addition it tries to avoid both communication and resource deadlocks and to check for livelocks. *

* TODO: not developed yet, dummy implementation only @@ -48,15 +48,15 @@ public RandomSwitchStrategy(RandomSwitchCTestConfiguration testCfg, Class tes } @Override - protected FailedIteration runImpl() { + protected LincheckFailure runImpl() { for (int i = 1; i < invocations; i++) { InvocationResult ir = runInvocation(); if (ir instanceof CompletedInvocationResult) { ExecutionResult results = ((CompletedInvocationResult) ir).getResults(); if (!verifier.verifyResults(getScenario(), results)) - return new IncorrectResultsFailedIteration(getScenario(), results); + return new IncorrectResultsFailure(getScenario(), results); } else { - return toFailedIteration(ir, getScenario()); + return toLincheckFailure(ir, getScenario()); } } return null; diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 412568975..d7594c7e3 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -50,7 +50,7 @@ class StressStrategy( runner = ParallelThreadsRunner(this, testClass, waits) } - override fun run(): FailedIteration? { + override fun run(): LincheckFailure? { try { // Run invocations for (invocation in 0 until invocations) { @@ -66,9 +66,9 @@ class StressStrategy( when (val ir = runner.run()) { is CompletedInvocationResult -> { if (!verifier.verifyResults(scenario, ir.results)) - return IncorrectResultsFailedIteration(scenario, ir.results) + return IncorrectResultsFailure(scenario, ir.results) } - else -> return ir.toFailedIteration(scenario) + else -> return ir.toLincheckFailure(scenario) } } return null diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java index dd105da03..25c56b962 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java @@ -33,7 +33,7 @@ * phase significantly. */ public abstract class CachedVerifier implements Verifier { - private final Map> previousResults = new HashMap<>(); + private final Map> previousResults = new WeakHashMap<>(); @Override public boolean verifyResults(ExecutionScenario scenario, ExecutionResult results) { diff --git a/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 9f501ed83..2a98d8e23 100644 --- a/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -25,6 +25,8 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* +import java.util.* +import kotlin.collections.ArrayList /** * This verifier tests for quiescent consistency. @@ -33,31 +35,51 @@ import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* * However, we believe that quiescent points do not occur * in practice while supporting them complicates the implementation. */ -class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : CachedVerifier() { +class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) + private val scenarioMapping: MutableMap = WeakHashMap() + override fun checkStateEquivalenceImplementation() = linearizabilityVerifier.checkStateEquivalenceImplementation() - override fun verifyResultsImpl(scenario: ExecutionScenario, results: ExecutionResult): Boolean { - val newThreads = scenario.threads + scenario.parallelExecution.flatten().count { it.isQuiescentConsistent } + override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean { + val convertedScenario = scenario.converted + val convertedResults = results.convert(scenario, convertedScenario.threads) + return linearizabilityVerifier.verifyResults(convertedScenario, convertedResults) + } + + private val ExecutionScenario.converted: ExecutionScenario get() = scenarioMapping.computeIfAbsent(this) { val parallelExecution = ArrayList>() - val parallelResults = ArrayList>() - repeat(scenario.threads) { + repeat(threads) { parallelExecution.add(ArrayList()) + } + parallelExecution.forEachIndexed { t, threadActors -> + for (a in threadActors) { + if (a.isQuiescentConsistent) { + parallelExecution.add(mutableListOf(a)) + } else { + parallelExecution[t].add(a) + } + } + } + ExecutionScenario(initExecution, parallelExecution, postExecution) + } + + private fun ExecutionResult.convert(originalScenario: ExecutionScenario, newThreads: Int): ExecutionResult { + val parallelResults = ArrayList>() + repeat(originalScenario.threads) { parallelResults.add(ArrayList()) } - val clocks = Array(scenario.threads) { ArrayList() } - val clockMapping = Array(scenario.threads) { ArrayList() } + val clocks = Array(originalScenario.threads) { ArrayList() } + val clockMapping = Array(originalScenario.threads) { ArrayList() } clockMapping.forEach { it.add(-1) } - scenario.parallelExecution.forEachIndexed { t, threadActors -> + originalScenario.parallelExecution.forEachIndexed { t, threadActors -> threadActors.forEachIndexed { i, a -> - val r = results.parallelResultsWithClock[t][i] + val r = parallelResultsWithClock[t][i] if (a.isQuiescentConsistent) { clockMapping[t].add(clockMapping[t][i]) - parallelExecution.add(mutableListOf(a)) parallelResults.add(mutableListOf(r.result.withEmptyClock(newThreads))) } else { clockMapping[t].add(clockMapping[t][i] + 1) - parallelExecution[t].add(a) val c = IntArray(newThreads) { 0 } clocks[t].add(c) parallelResults[t].add(ResultWithClock(r.result, HBClock(c))) @@ -66,17 +88,15 @@ class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : CachedVe } clocks.forEachIndexed { t, threadClocks -> threadClocks.forEachIndexed { i, c -> - for (j in 0 until scenario.threads) { - val old = results.parallelResultsWithClock[t][i].clockOnStart[j] + for (j in 0 until originalScenario.threads) { + val old = parallelResultsWithClock[t][i].clockOnStart[j] c[j] = clockMapping[j][old] } } } - val convertedScenario = ExecutionScenario(scenario.initExecution, parallelExecution, scenario.postExecution) - val convertedResults = ExecutionResult(results.initResults, parallelResults, results.postResults) - return linearizabilityVerifier.verifyResultsImpl(convertedScenario, convertedResults) + return ExecutionResult(initResults, parallelResults, postResults) } -} + } private val Actor.isQuiescentConsistent: Boolean get() = method.isAnnotationPresent(QuiescentConsistent::class.java) diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt index 57bcebce1..78c59dac5 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt @@ -29,20 +29,20 @@ import org.junit.* import kotlin.reflect.* abstract class AbstractLincheckTest( - private vararg val expectedErrors: KClass + private vararg val expectedErrors: KClass ) : VerifierState() { open fun > O.customize() {} override fun extractState(): Any = System.identityHashCode(this) private fun > O.runInternalTest() { - val failedIteration: FailedIteration? = checkImpl(this@AbstractLincheckTest::class.java) - if (failedIteration === null) { + val failure: LincheckFailure? = checkImpl(this@AbstractLincheckTest::class.java) + if (failure === null) { assert(expectedErrors.isEmpty()) { "This test should fail, but no error has been occurred (see the logs for details)" } } else { - assert(expectedErrors.contains(failedIteration::class)) { - "This test has been failed with an unexpected error: \n $failedIteration" + assert(expectedErrors.contains(failure::class)) { + "This test has been failed with an unexpected error: \n $failure" } } } diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt index b1685980b..50f3885d9 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.strategy.* -class HangingTest : AbstractLincheckTest(DeadlockedWithDumpFailedIteration::class) { +class HangingTest : AbstractLincheckTest(DeadlockWithDumpFailure::class) { @Operation fun badOperation() { while (true) {} diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt index 46f1d1136..992f98091 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/DeadlockOnSynchronizedTest.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.test.AbstractLincheckTest -class DeadlockOnSynchronizedTest : AbstractLincheckTest(DeadlockedWithDumpFailedIteration::class) { +class DeadlockOnSynchronizedTest : AbstractLincheckTest(DeadlockWithDumpFailure::class) { private var counter = 0 private var lock1 = Any() private var lock2 = Any() diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt index b540a6412..b6574b5ed 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt @@ -147,5 +147,5 @@ class ParallelThreadsRunnerExceptionTest { } fun mockStrategy(scenario: ExecutionScenario) = object : Strategy(scenario) { - override fun run(): FailedIteration? = error("Not yet implemented") + override fun run(): LincheckFailure? = error("Not yet implemented") } \ No newline at end of file diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 75e3386f5..b69a20454 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -41,7 +41,7 @@ public void setUp() { ExecutionScenario scenario = new ExecutionScenario(emptyList(), emptyList(), emptyList()); Strategy strategy = new Strategy(scenario) { @Override - public FailedIteration run() { + public LincheckFailure run() { throw new UnsupportedOperationException(); } }; diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt index bc92ac0fe..ca154fb07 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/SequentialSpecificationTest.kt @@ -24,12 +24,11 @@ package org.jetbrains.kotlinx.lincheck.test.verifier import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.strategy.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.test.* import org.jetbrains.kotlinx.lincheck.verifier.* import java.util.concurrent.atomic.* -class SequentialSpecificationTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class) { +class SequentialSpecificationTest : AbstractLincheckTest(IncorrectResultsFailure::class) { private val c = AtomicInteger() @Operation diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt index 70f0b7ff5..01d924156 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ConcurrentDequeTest.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.test.* import java.util.concurrent.* @Param(name = "value", gen = IntGen::class, conf = "1:5") -class ConcurrentDequeTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class) { +class ConcurrentDequeTest : AbstractLincheckTest(IncorrectResultsFailure::class) { private val deque = ConcurrentLinkedDeque() @Operation diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt index ea82b155d..67f69494f 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/CounterTests.kt @@ -29,7 +29,7 @@ import kotlin.reflect.KClass abstract class AbstractCounterTest( private val counter: Counter, - vararg expectedErrors: KClass + vararg expectedErrors: KClass ) : AbstractLincheckTest(*expectedErrors) { @Operation fun incAndGet(): Int = counter.incAndGet() @@ -38,9 +38,9 @@ abstract class AbstractCounterTest( } class CounterCorrectTest : AbstractCounterTest(CounterCorrect()) -class CounterWrong0Test : AbstractCounterTest(CounterWrong0(), IncorrectResultsFailedIteration::class) -class CounterWrong1Test : AbstractCounterTest(CounterWrong1(), IncorrectResultsFailedIteration::class) -class CounterWrong2Test : AbstractCounterTest(CounterWrong2(), IncorrectResultsFailedIteration::class) +class CounterWrong0Test : AbstractCounterTest(CounterWrong0(), IncorrectResultsFailure::class) +class CounterWrong1Test : AbstractCounterTest(CounterWrong1(), IncorrectResultsFailure::class) +class CounterWrong2Test : AbstractCounterTest(CounterWrong2(), IncorrectResultsFailure::class) interface Counter { fun incAndGet(): Int diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt index a3f806203..41fa369f2 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/HashMapTest.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.test.* import java.util.* @Param(name = "key", gen = IntGen::class) -class HashMapTest : AbstractLincheckTest(IncorrectResultsFailedIteration::class, UnexpectedExceptionFailedIteration::class) { +class HashMapTest : AbstractLincheckTest(IncorrectResultsFailure::class, UnexpectedExceptionFailure::class) { private val m = HashMap() @Operation From 666a832df7ab7928038d1b4d56258b98123e6737 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 13:32:54 +0300 Subject: [PATCH 3/6] Fix `FailedScenarioMinimizationTest` --- .../lincheck/test/FailedScenarioMinimizationTest.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt index e696c539a..bb5332ee8 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt @@ -46,7 +46,9 @@ class FailedScenarioMinimizationTest: VerifierState() { @Test fun testWithoutMinimization() { - val options = StressOptions().actorsPerThread(15).minimizeFailedScenario(false) + val options = StressOptions().invocationsPerIteration(100_000) + .actorsPerThread(15) + .minimizeFailedScenario(false) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) fail("Should fail with AssertionError") @@ -61,7 +63,8 @@ class FailedScenarioMinimizationTest: VerifierState() { @Test fun testWithMinimization() { - val options = StressOptions().actorsPerThread(15) // minimizeFailedScenario == true by default + val options = StressOptions().invocationsPerIteration(100_000) + .actorsPerThread(15) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) fail("Should fail with AssertionError") From b1988460c43cfd9fe736eb2bfaca1b2a58066acd Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 13:46:45 +0300 Subject: [PATCH 4/6] Fix `FailedScenarioMinimizationTest` --- .../test/FailedScenarioMinimizationTest.kt | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt index bb5332ee8..92f90db25 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt @@ -30,24 +30,18 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import org.junit.* import org.junit.Assert.* -@Param(name = "key", gen = IntGen::class, conf = "1:5") class FailedScenarioMinimizationTest: VerifierState() { - private val m = HashMap() - override fun extractState() = m + @Volatile + private var counter = 0 @Operation - fun put(@Param(name = "key") key: Int, @Param(gen = IntGen::class) value: Int) = m.put(key, value) + fun inc() = counter++ - @Operation - fun get(@Param(name = "key") key: Int) = m.get(key) - - @Operation - fun remove(@Param(name = "key") key: Int) = m.remove(key) + override fun extractState() = counter @Test fun testWithoutMinimization() { - val options = StressOptions().invocationsPerIteration(100_000) - .actorsPerThread(15) + val options = StressOptions().actorsPerThread(10) .minimizeFailedScenario(false) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) @@ -57,14 +51,13 @@ class FailedScenarioMinimizationTest: VerifierState() { assertTrue("The init part should NOT be minimized", m.contains("Init")) assertTrue("The post part should NOT be minimized", m.contains("Post")) assertEquals("The parallel part should NOT be minimized", - 15, m.lines().filter { it.contains("|") }.size) + 10, m.lines().filter { it.contains("|") }.size) } } @Test fun testWithMinimization() { - val options = StressOptions().invocationsPerIteration(100_000) - .actorsPerThread(15) + val options = StressOptions().actorsPerThread(10) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) fail("Should fail with AssertionError") From e755e605aa00a7680cc45a17476d8c338e6ee083 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 13:47:24 +0300 Subject: [PATCH 5/6] Small refactoring --- .../jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt index 78c59dac5..b9569583b 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLincheckTest.kt @@ -29,7 +29,7 @@ import org.junit.* import kotlin.reflect.* abstract class AbstractLincheckTest( - private vararg val expectedErrors: KClass + private vararg val expectedFailures: KClass ) : VerifierState() { open fun > O.customize() {} override fun extractState(): Any = System.identityHashCode(this) @@ -37,11 +37,11 @@ abstract class AbstractLincheckTest( private fun > O.runInternalTest() { val failure: LincheckFailure? = checkImpl(this@AbstractLincheckTest::class.java) if (failure === null) { - assert(expectedErrors.isEmpty()) { + assert(expectedFailures.isEmpty()) { "This test should fail, but no error has been occurred (see the logs for details)" } } else { - assert(expectedErrors.contains(failure::class)) { + assert(expectedFailures.contains(failure::class)) { "This test has been failed with an unexpected error: \n $failure" } } From 8ae7c758f00ed01145ee29135c01386fb717d9f3 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 16 Apr 2020 14:00:00 +0300 Subject: [PATCH 6/6] Small refactoring --- .../kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt index 92f90db25..ae176311b 100644 --- a/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt +++ b/src/test/java/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt @@ -42,6 +42,7 @@ class FailedScenarioMinimizationTest: VerifierState() { @Test fun testWithoutMinimization() { val options = StressOptions().actorsPerThread(10) + .invocationsPerIteration(100_000) .minimizeFailedScenario(false) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) @@ -58,6 +59,7 @@ class FailedScenarioMinimizationTest: VerifierState() { @Test fun testWithMinimization() { val options = StressOptions().actorsPerThread(10) + .invocationsPerIteration(100_000) try { LinChecker.check(FailedScenarioMinimizationTest::class.java, options) fail("Should fail with AssertionError")