summaryrefslogtreecommitdiffstats
path: root/luni/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/java')
-rw-r--r--luni/src/main/java/java/beans/PropertyChangeSupport.java2
-rw-r--r--luni/src/main/java/java/io/BufferedWriter.java22
-rw-r--r--luni/src/main/java/java/io/ByteArrayOutputStream.java8
-rw-r--r--luni/src/main/java/java/io/File.java68
-rw-r--r--luni/src/main/java/java/io/FileDescriptor.java6
-rw-r--r--luni/src/main/java/java/io/InputStreamReader.java18
-rw-r--r--luni/src/main/java/java/io/ObjectInputStream.java11
-rw-r--r--luni/src/main/java/java/io/ObjectStreamClass.java8
-rw-r--r--luni/src/main/java/java/io/ObjectStreamField.java14
-rw-r--r--luni/src/main/java/java/io/OutputStreamWriter.java28
-rw-r--r--luni/src/main/java/java/io/PipedOutputStream.java2
-rw-r--r--luni/src/main/java/java/io/PipedWriter.java2
-rw-r--r--luni/src/main/java/java/io/PrintStream.java60
-rw-r--r--luni/src/main/java/java/io/Reader.java2
-rw-r--r--luni/src/main/java/java/io/SequenceInputStream.java6
-rw-r--r--luni/src/main/java/java/io/StreamTokenizer.java4
-rw-r--r--luni/src/main/java/java/io/StringBufferInputStream.java2
-rw-r--r--luni/src/main/java/java/io/Writer.java2
-rw-r--r--luni/src/main/java/java/lang/AbstractStringBuilder.java4
-rw-r--r--luni/src/main/java/java/lang/Character.java20
-rw-r--r--luni/src/main/java/java/lang/ClassLoader.java2
-rw-r--r--luni/src/main/java/java/lang/Daemons.java100
-rw-r--r--luni/src/main/java/java/lang/Enum.java14
-rw-r--r--luni/src/main/java/java/lang/Object.java2
-rw-r--r--luni/src/main/java/java/lang/ProcessBuilder.java4
-rw-r--r--luni/src/main/java/java/lang/ProcessManager.java16
-rw-r--r--luni/src/main/java/java/lang/Runtime.java23
-rw-r--r--luni/src/main/java/java/lang/StackTraceElement.java6
-rw-r--r--luni/src/main/java/java/lang/String.java22
-rw-r--r--luni/src/main/java/java/lang/StringBuilder.java6
-rw-r--r--luni/src/main/java/java/lang/System.java8
-rw-r--r--luni/src/main/java/java/lang/Thread.java12
-rw-r--r--luni/src/main/java/java/lang/Throwable.java22
-rw-r--r--luni/src/main/java/java/lang/ref/FinalizerReference.java68
-rw-r--r--luni/src/main/java/java/lang/ref/Reference.java5
-rw-r--r--luni/src/main/java/java/lang/ref/ReferenceQueue.java4
-rw-r--r--luni/src/main/java/java/lang/reflect/GenericArrayType.java2
-rw-r--r--luni/src/main/java/java/lang/reflect/Proxy.java8
-rw-r--r--luni/src/main/java/java/math/BigDecimal.java8
-rw-r--r--luni/src/main/java/java/math/BigInt.java2
-rw-r--r--luni/src/main/java/java/net/CookieStore.java2
-rw-r--r--luni/src/main/java/java/net/DatagramSocket.java6
-rw-r--r--luni/src/main/java/java/net/NetworkInterface.java18
-rw-r--r--luni/src/main/java/java/net/PlainDatagramSocketImpl.java2
-rw-r--r--luni/src/main/java/java/net/URISyntaxException.java12
-rw-r--r--luni/src/main/java/java/net/URLClassLoader.java2
-rw-r--r--luni/src/main/java/java/net/URLConnection.java12
-rw-r--r--luni/src/main/java/java/nio/Buffer.java2
-rw-r--r--luni/src/main/java/java/nio/ByteBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/CharBuffer.java6
-rw-r--r--luni/src/main/java/java/nio/DatagramChannelImpl.java2
-rw-r--r--luni/src/main/java/java/nio/DoubleBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/FileChannelImpl.java4
-rw-r--r--luni/src/main/java/java/nio/FloatBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/IntBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/LongBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/MappedByteBuffer.java2
-rw-r--r--luni/src/main/java/java/nio/NIOAccess.java13
-rw-r--r--luni/src/main/java/java/nio/PipeImpl.java46
-rw-r--r--luni/src/main/java/java/nio/SelectorImpl.java2
-rw-r--r--luni/src/main/java/java/nio/SelectorProviderImpl.java2
-rw-r--r--luni/src/main/java/java/nio/ShortBuffer.java4
-rw-r--r--luni/src/main/java/java/nio/SocketChannelImpl.java13
-rw-r--r--luni/src/main/java/java/nio/channels/Channels.java12
-rw-r--r--luni/src/main/java/java/nio/channels/FileLock.java2
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetDecoder.java4
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetDecoderICU.java37
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetEncoder.java4
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetEncoderICU.java31
-rw-r--r--luni/src/main/java/java/nio/charset/Charsets.java2
-rw-r--r--luni/src/main/java/java/nio/charset/CoderResult.java4
-rw-r--r--luni/src/main/java/java/security/AlgorithmParameterGenerator.java4
-rw-r--r--luni/src/main/java/java/security/AlgorithmParameters.java8
-rw-r--r--luni/src/main/java/java/security/KeyFactory.java4
-rw-r--r--luni/src/main/java/java/security/KeyPairGenerator.java4
-rw-r--r--luni/src/main/java/java/security/KeyStore.java4
-rw-r--r--luni/src/main/java/java/security/KeyStoreSpi.java16
-rw-r--r--luni/src/main/java/java/security/MessageDigest.java6
-rw-r--r--luni/src/main/java/java/security/Provider.java108
-rw-r--r--luni/src/main/java/java/security/SecureRandom.java5
-rw-r--r--luni/src/main/java/java/security/Security.java4
-rw-r--r--luni/src/main/java/java/security/Signature.java6
-rw-r--r--luni/src/main/java/java/security/Signer.java2
-rw-r--r--luni/src/main/java/java/security/cert/CertPathBuilder.java8
-rw-r--r--luni/src/main/java/java/security/cert/CertPathValidator.java4
-rw-r--r--luni/src/main/java/java/security/cert/CertStore.java8
-rw-r--r--luni/src/main/java/java/security/cert/CertificateFactory.java8
-rw-r--r--luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java6
-rw-r--r--luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java12
-rw-r--r--luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java4
-rw-r--r--luni/src/main/java/java/security/cert/X509CRL.java2
-rw-r--r--luni/src/main/java/java/security/spec/ECGenParameterSpec.java2
-rw-r--r--luni/src/main/java/java/sql/DataTruncation.java4
-rw-r--r--luni/src/main/java/java/sql/DriverManager.java2
-rw-r--r--luni/src/main/java/java/text/AttributedString.java8
-rw-r--r--luni/src/main/java/java/text/Collator.java2
-rw-r--r--luni/src/main/java/java/text/DateFormatSymbols.java6
-rw-r--r--luni/src/main/java/java/text/DecimalFormat.java4
-rw-r--r--luni/src/main/java/java/text/DecimalFormatSymbols.java6
-rw-r--r--luni/src/main/java/java/text/MessageFormat.java2
-rw-r--r--luni/src/main/java/java/text/NumberFormat.java2
-rw-r--r--luni/src/main/java/java/text/RuleBasedCollator.java12
-rw-r--r--luni/src/main/java/java/text/SimpleDateFormat.java56
-rw-r--r--luni/src/main/java/java/util/AbstractQueue.java6
-rw-r--r--luni/src/main/java/java/util/ArrayDeque.java70
-rw-r--r--luni/src/main/java/java/util/Arrays.java24
-rw-r--r--luni/src/main/java/java/util/BitSet.java2
-rw-r--r--luni/src/main/java/java/util/Calendar.java12
-rw-r--r--luni/src/main/java/java/util/Collections.java46
-rw-r--r--luni/src/main/java/java/util/Deque.java5
-rw-r--r--luni/src/main/java/java/util/DualPivotQuicksort.java4
-rw-r--r--luni/src/main/java/java/util/DuplicateFormatFlagsException.java2
-rw-r--r--luni/src/main/java/java/util/EnumMap.java2
-rw-r--r--luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java2
-rw-r--r--luni/src/main/java/java/util/Formatter.java2
-rw-r--r--luni/src/main/java/java/util/GregorianCalendar.java233
-rw-r--r--luni/src/main/java/java/util/Hashtable.java16
-rw-r--r--luni/src/main/java/java/util/IllegalFormatConversionException.java2
-rw-r--r--luni/src/main/java/java/util/IllegalFormatFlagsException.java2
-rw-r--r--luni/src/main/java/java/util/ListResourceBundle.java2
-rw-r--r--luni/src/main/java/java/util/Locale.java11
-rw-r--r--luni/src/main/java/java/util/MissingFormatArgumentException.java2
-rw-r--r--luni/src/main/java/java/util/MissingFormatWidthException.java2
-rw-r--r--luni/src/main/java/java/util/NavigableMap.java9
-rw-r--r--luni/src/main/java/java/util/NavigableSet.java9
-rw-r--r--luni/src/main/java/java/util/Observable.java2
-rw-r--r--luni/src/main/java/java/util/PriorityQueue.java4
-rw-r--r--luni/src/main/java/java/util/Properties.java12
-rw-r--r--luni/src/main/java/java/util/PropertyResourceBundle.java2
-rw-r--r--luni/src/main/java/java/util/Queue.java2
-rw-r--r--luni/src/main/java/java/util/Random.java18
-rw-r--r--luni/src/main/java/java/util/ResourceBundle.java48
-rw-r--r--luni/src/main/java/java/util/Scanner.java6
-rw-r--r--luni/src/main/java/java/util/ServiceLoader.java2
-rw-r--r--luni/src/main/java/java/util/SimpleTimeZone.java13
-rw-r--r--luni/src/main/java/java/util/StringTokenizer.java18
-rw-r--r--luni/src/main/java/java/util/TimeZone.java158
-rw-r--r--luni/src/main/java/java/util/Timer.java2
-rw-r--r--luni/src/main/java/java/util/UUID.java4
-rw-r--r--luni/src/main/java/java/util/UnknownFormatConversionException.java2
-rw-r--r--luni/src/main/java/java/util/UnknownFormatFlagsException.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/AbstractExecutorService.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java849
-rw-r--r--luni/src/main/java/java/util/concurrent/BlockingDeque.java72
-rw-r--r--luni/src/main/java/java/util/concurrent/BlockingQueue.java23
-rw-r--r--luni/src/main/java/java/util/concurrent/BrokenBarrierException.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/Callable.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/CancellationException.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/CompletionService.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java1149
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java89
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java66
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentMap.java53
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java10
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java213
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java61
-rw-r--r--luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java7
-rw-r--r--luni/src/main/java/java/util/concurrent/CountDownLatch.java15
-rw-r--r--luni/src/main/java/java/util/concurrent/CyclicBarrier.java48
-rw-r--r--luni/src/main/java/java/util/concurrent/DelayQueue.java81
-rw-r--r--luni/src/main/java/java/util/concurrent/Delayed.java4
-rw-r--r--luni/src/main/java/java/util/concurrent/Exchanger.java15
-rw-r--r--luni/src/main/java/java/util/concurrent/ExecutionException.java10
-rw-r--r--luni/src/main/java/java/util/concurrent/Executor.java22
-rw-r--r--luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/ExecutorService.java18
-rw-r--r--luni/src/main/java/java/util/concurrent/Executors.java114
-rw-r--r--luni/src/main/java/java/util/concurrent/ForkJoinPool.java2127
-rw-r--r--luni/src/main/java/java/util/concurrent/ForkJoinTask.java1357
-rw-r--r--luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java970
-rw-r--r--luni/src/main/java/java/util/concurrent/Future.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/FutureTask.java532
-rw-r--r--luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java25
-rw-r--r--luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java22
-rw-r--r--luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java1323
-rw-r--r--luni/src/main/java/java/util/concurrent/Phaser.java1135
-rw-r--r--luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java112
-rw-r--r--luni/src/main/java/java/util/concurrent/RecursiveAction.java165
-rw-r--r--luni/src/main/java/java/util/concurrent/RecursiveTask.java69
-rw-r--r--luni/src/main/java/java/util/concurrent/RejectedExecutionException.java6
-rw-r--r--luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/RunnableFuture.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java4
-rw-r--r--luni/src/main/java/java/util/concurrent/ScheduledFuture.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java125
-rw-r--r--luni/src/main/java/java/util/concurrent/Semaphore.java9
-rw-r--r--luni/src/main/java/java/util/concurrent/SynchronousQueue.java113
-rw-r--r--luni/src/main/java/java/util/concurrent/ThreadFactory.java7
-rw-r--r--luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java198
-rw-r--r--luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java22
-rw-r--r--luni/src/main/java/java/util/concurrent/TimeUnit.java16
-rw-r--r--luni/src/main/java/java/util/concurrent/TimeoutException.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/TransferQueue.java133
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java12
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java27
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java5
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java15
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java27
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java5
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java18
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java6
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java12
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java48
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java10
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java4
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/Fences.java540
-rw-r--r--luni/src/main/java/java/util/concurrent/atomic/package-info.java31
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java7
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java19
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/Condition.java9
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/Lock.java42
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/LockSupport.java16
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java30
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java107
-rw-r--r--luni/src/main/java/java/util/concurrent/locks/package-info.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/package-info.java11
-rw-r--r--luni/src/main/java/java/util/jar/JarOutputStream.java12
-rw-r--r--luni/src/main/java/java/util/logging/FileHandler.java6
-rw-r--r--luni/src/main/java/java/util/logging/Handler.java16
-rw-r--r--luni/src/main/java/java/util/logging/LogManager.java2
-rw-r--r--luni/src/main/java/java/util/logging/StreamHandler.java2
-rw-r--r--luni/src/main/java/java/util/prefs/AbstractPreferences.java18
-rw-r--r--luni/src/main/java/java/util/regex/Matcher.java40
-rw-r--r--luni/src/main/java/java/util/regex/Pattern.java21
-rw-r--r--luni/src/main/java/java/util/zip/DeflaterInputStream.java6
-rw-r--r--luni/src/main/java/java/util/zip/DeflaterOutputStream.java6
-rw-r--r--luni/src/main/java/java/util/zip/InflaterInputStream.java6
-rw-r--r--luni/src/main/java/java/util/zip/InflaterOutputStream.java6
-rw-r--r--luni/src/main/java/java/util/zip/ZipEntry.java2
-rw-r--r--luni/src/main/java/java/util/zip/ZipFile.java2
-rw-r--r--luni/src/main/java/java/util/zip/ZipInputStream.java2
-rw-r--r--luni/src/main/java/javax/crypto/Cipher.java8
-rw-r--r--luni/src/main/java/javax/crypto/CipherInputStream.java2
-rw-r--r--luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java10
-rw-r--r--luni/src/main/java/javax/crypto/ExemptionMechanism.java6
-rw-r--r--luni/src/main/java/javax/crypto/KeyAgreement.java4
-rw-r--r--luni/src/main/java/javax/crypto/KeyGenerator.java4
-rw-r--r--luni/src/main/java/javax/crypto/Mac.java4
-rw-r--r--luni/src/main/java/javax/crypto/SealedObject.java30
-rw-r--r--luni/src/main/java/javax/crypto/SecretKeyFactory.java4
-rw-r--r--luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java4
-rw-r--r--luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java8
-rw-r--r--luni/src/main/java/javax/crypto/spec/PSource.java4
-rw-r--r--luni/src/main/java/javax/net/ssl/KeyManagerFactory.java4
-rw-r--r--luni/src/main/java/javax/net/ssl/SSLContext.java4
-rw-r--r--luni/src/main/java/javax/net/ssl/TrustManagerFactory.java4
-rw-r--r--luni/src/main/java/javax/security/auth/Subject.java18
-rw-r--r--luni/src/main/java/javax/security/auth/x500/X500Principal.java4
-rw-r--r--luni/src/main/java/javax/xml/datatype/DatatypeFactory.java4
-rw-r--r--luni/src/main/java/javax/xml/datatype/Duration.java6
-rw-r--r--luni/src/main/java/javax/xml/namespace/QName.java2
-rw-r--r--luni/src/main/java/javax/xml/validation/SchemaFactory.java14
-rw-r--r--luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java4
-rw-r--r--luni/src/main/java/javax/xml/validation/Validator.java16
-rw-r--r--luni/src/main/java/javax/xml/validation/ValidatorHandler.java20
-rw-r--r--luni/src/main/java/javax/xml/xpath/XPathException.java8
-rw-r--r--luni/src/main/java/javax/xml/xpath/XPathFactory.java8
-rw-r--r--luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java4
-rw-r--r--luni/src/main/java/javax/xml/xpath/package.html2
-rw-r--r--luni/src/main/java/libcore/icu/ErrorCode.java71
-rw-r--r--luni/src/main/java/libcore/icu/ICU.java13
-rw-r--r--luni/src/main/java/libcore/icu/LocaleData.java73
-rw-r--r--luni/src/main/java/libcore/icu/NativeConverter.java24
-rw-r--r--luni/src/main/java/libcore/icu/NativeIDN.java2
-rw-r--r--luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java2
-rw-r--r--luni/src/main/java/libcore/io/BlockGuardOs.java18
-rw-r--r--luni/src/main/java/libcore/io/DiskLruCache.java12
-rw-r--r--luni/src/main/java/libcore/io/DropBox.java72
-rw-r--r--luni/src/main/java/libcore/io/EventLogger.java69
-rw-r--r--luni/src/main/java/libcore/io/ForwardingOs.java23
-rw-r--r--luni/src/main/java/libcore/io/Os.java23
-rw-r--r--luni/src/main/java/libcore/io/OsConstants.java1
-rw-r--r--luni/src/main/java/libcore/io/Posix.java27
-rw-r--r--luni/src/main/java/libcore/io/StrictLineReader.java6
-rw-r--r--luni/src/main/java/libcore/net/MimeUtils.java1
-rw-r--r--luni/src/main/java/libcore/net/UriCodec.java2
-rw-r--r--luni/src/main/java/libcore/net/http/HttpConnection.java1
-rw-r--r--luni/src/main/java/libcore/net/http/HttpEngine.java30
-rw-r--r--luni/src/main/java/libcore/net/http/HttpResponseCache.java2
-rw-r--r--luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java9
-rw-r--r--luni/src/main/java/libcore/net/http/ResponseHeaders.java5
-rw-r--r--luni/src/main/java/libcore/util/BasicLruCache.java8
-rw-r--r--luni/src/main/java/libcore/util/Objects.java64
-rw-r--r--luni/src/main/java/libcore/util/ZoneInfo.java44
-rw-r--r--luni/src/main/java/libcore/util/ZoneInfoDB.java13
-rw-r--r--luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java6
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java566
-rw-r--r--luni/src/main/java/org/apache/harmony/security/SystemScope.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/security/fortress/Engine.java12
-rw-r--r--luni/src/main/java/org/apache/harmony/security/fortress/Services.java184
-rw-r--r--luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java34
-rw-r--r--luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java7
-rw-r--r--luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java16
-rw-r--r--luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java184
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java28
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java94
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java356
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java17
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java10
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java70
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java47
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java18
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java41
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java24
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java30
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java196
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java130
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java (renamed from luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java)23
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java70
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java155
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java (renamed from luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java)26
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java37
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java5
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java138
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java125
328 files changed, 13954 insertions, 4098 deletions
diff --git a/luni/src/main/java/java/beans/PropertyChangeSupport.java b/luni/src/main/java/java/beans/PropertyChangeSupport.java
index 04f8155..1db12b7 100644
--- a/luni/src/main/java/java/beans/PropertyChangeSupport.java
+++ b/luni/src/main/java/java/beans/PropertyChangeSupport.java
@@ -66,7 +66,7 @@ public class PropertyChangeSupport implements Serializable {
*/
public PropertyChangeSupport(Object sourceBean) {
if (sourceBean == null) {
- throw new NullPointerException();
+ throw new NullPointerException("sourceBean == null");
}
this.sourceBean = sourceBean;
}
diff --git a/luni/src/main/java/java/io/BufferedWriter.java b/luni/src/main/java/java/io/BufferedWriter.java
index e4cbe7c..55ae121 100644
--- a/luni/src/main/java/java/io/BufferedWriter.java
+++ b/luni/src/main/java/java/io/BufferedWriter.java
@@ -163,33 +163,33 @@ public class BufferedWriter extends Writer {
/**
* Writes {@code count} characters starting at {@code offset} in
- * {@code cbuf} to this writer. If {@code count} is greater than this
+ * {@code buffer} to this writer. If {@code count} is greater than this
* writer's buffer, then the buffer is flushed and the characters are
* written directly to the target writer.
*
- * @param cbuf
+ * @param buffer
* the array containing characters to write.
* @param offset
- * the start position in {@code cbuf} for retrieving characters.
+ * the start position in {@code buffer} for retrieving characters.
* @param count
* the maximum number of characters to write.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code count < 0}, or if
* {@code offset + count} is greater than the size of
- * {@code cbuf}.
+ * {@code buffer}.
* @throws IOException
* if this writer is closed or another I/O error occurs.
*/
@Override
- public void write(char[] cbuf, int offset, int count) throws IOException {
+ public void write(char[] buffer, int offset, int count) throws IOException {
synchronized (lock) {
checkNotClosed();
- if (cbuf == null) {
+ if (buffer == null) {
throw new NullPointerException("buffer == null");
}
- Arrays.checkOffsetAndCount(cbuf.length, offset, count);
+ Arrays.checkOffsetAndCount(buffer.length, offset, count);
if (pos == 0 && count >= this.buf.length) {
- out.write(cbuf, offset, count);
+ out.write(buffer, offset, count);
return;
}
int available = this.buf.length - pos;
@@ -197,7 +197,7 @@ public class BufferedWriter extends Writer {
available = count;
}
if (available > 0) {
- System.arraycopy(cbuf, offset, this.buf, pos, available);
+ System.arraycopy(buffer, offset, this.buf, pos, available);
pos += available;
}
if (pos == this.buf.length) {
@@ -207,11 +207,11 @@ public class BufferedWriter extends Writer {
offset += available;
available = count - available;
if (available >= this.buf.length) {
- out.write(cbuf, offset, available);
+ out.write(buffer, offset, available);
return;
}
- System.arraycopy(cbuf, offset, this.buf, pos, available);
+ System.arraycopy(buffer, offset, this.buf, pos, available);
pos += available;
}
}
diff --git a/luni/src/main/java/java/io/ByteArrayOutputStream.java b/luni/src/main/java/java/io/ByteArrayOutputStream.java
index 3ab2c20..ff9c7df 100644
--- a/luni/src/main/java/java/io/ByteArrayOutputStream.java
+++ b/luni/src/main/java/java/io/ByteArrayOutputStream.java
@@ -162,17 +162,17 @@ public class ByteArrayOutputStream extends OutputStream {
/**
* Returns the contents of this ByteArrayOutputStream as a string converted
- * according to the encoding declared in {@code enc}.
+ * according to the encoding declared in {@code charsetName}.
*
- * @param enc
+ * @param charsetName
* a string representing the encoding to use when translating
* this stream to a string.
* @return this stream's current contents as an encoded string.
* @throws UnsupportedEncodingException
* if the provided encoding is not supported.
*/
- public String toString(String enc) throws UnsupportedEncodingException {
- return new String(buf, 0, count, enc);
+ public String toString(String charsetName) throws UnsupportedEncodingException {
+ return new String(buf, 0, count, charsetName);
}
/**
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index 968f021..ec87fed 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -147,7 +147,7 @@ public class File implements Serializable, Comparable<File> {
*/
public File(String dirPath, String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (dirPath == null || dirPath.isEmpty()) {
this.path = fixSlashes(name);
@@ -855,57 +855,65 @@ public class File implements Serializable, Comparable<File> {
}
/**
- * Creates the directory named by the trailing filename of this file. Does
- * not create the complete path required to create this directory.
+ * Creates the directory named by this file, assuming its parents exist.
+ * Use {@link #mkdirs} if you also want to create missing parents.
*
* <p>Note that this method does <i>not</i> throw {@code IOException} on failure.
- * Callers must check the return value.
+ * Callers must check the return value. Note also that this method returns
+ * false if the directory already existed. If you want to know whether the
+ * directory exists on return, either use {@code (f.mkdir() || f.isDirectory())}
+ * or simply ignore the return value from this method and simply call {@link #isDirectory}.
*
- * @return {@code true} if the directory has been created, {@code false}
- * otherwise.
- * @see #mkdirs
+ * @return {@code true} if the directory was created,
+ * {@code false} on failure or if the directory already existed.
*/
public boolean mkdir() {
try {
- // On Android, we don't want default permissions to allow global access.
- Libcore.os.mkdir(path, S_IRWXU);
+ mkdirErrno();
return true;
} catch (ErrnoException errnoException) {
return false;
}
}
+ private void mkdirErrno() throws ErrnoException {
+ // On Android, we don't want default permissions to allow global access.
+ Libcore.os.mkdir(path, S_IRWXU);
+ }
+
/**
- * Creates the directory named by the trailing filename of this file,
- * including the complete directory path required to create this directory.
+ * Creates the directory named by this file, creating missing parent
+ * directories if necessary.
+ * Use {@link #mkdir} if you don't want to create missing parents.
*
* <p>Note that this method does <i>not</i> throw {@code IOException} on failure.
- * Callers must check the return value.
+ * Callers must check the return value. Note also that this method returns
+ * false if the directory already existed. If you want to know whether the
+ * directory exists on return, either use {@code (f.mkdirs() || f.isDirectory())}
+ * or simply ignore the return value from this method and simply call {@link #isDirectory}.
*
- * @return {@code true} if the necessary directories have been created,
- * {@code false} if the target directory already exists or one of
- * the directories can not be created.
- * @see #mkdir
+ * @return {@code true} if the directory was created,
+ * {@code false} on failure or if the directory already existed.
*/
public boolean mkdirs() {
- /* If the terminal directory already exists, answer false */
- if (exists()) {
- return false;
- }
+ return mkdirs(false);
+ }
- /* If the receiver can be created, answer true */
- if (mkdir()) {
+ private boolean mkdirs(boolean resultIfExists) {
+ try {
+ // Try to create the directory directly.
+ mkdirErrno();
return true;
- }
-
- String parentDir = getParent();
- /* If there is no parent and we were not created, answer false */
- if (parentDir == null) {
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == ENOENT) {
+ // If the parent was missing, try to create it and then try again.
+ File parent = getParentFile();
+ return parent != null && parent.mkdirs(true) && mkdir();
+ } else if (errnoException.errno == EEXIST) {
+ return resultIfExists;
+ }
return false;
}
-
- /* Otherwise, try to create a parent directory and then this directory */
- return (new File(parentDir).mkdirs() && mkdir());
}
/**
diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java
index f04ae2c..e4eb06c 100644
--- a/luni/src/main/java/java/io/FileDescriptor.java
+++ b/luni/src/main/java/java/io/FileDescriptor.java
@@ -68,7 +68,11 @@ public final class FileDescriptor {
*/
public void sync() throws SyncFailedException {
try {
- Libcore.os.fsync(this);
+ if (Libcore.os.isatty(this)) {
+ Libcore.os.tcdrain(this);
+ } else {
+ Libcore.os.fsync(this);
+ }
} catch (ErrnoException errnoException) {
SyncFailedException sfe = new SyncFailedException(errnoException.getMessage());
sfe.initCause(errnoException);
diff --git a/luni/src/main/java/java/io/InputStreamReader.java b/luni/src/main/java/java/io/InputStreamReader.java
index 59be9ed..d3650dc 100644
--- a/luni/src/main/java/java/io/InputStreamReader.java
+++ b/luni/src/main/java/java/io/InputStreamReader.java
@@ -62,32 +62,32 @@ public class InputStreamReader extends Reader {
/**
* Constructs a new InputStreamReader on the InputStream {@code in}. The
* character converter that is used to decode bytes into characters is
- * identified by name by {@code enc}. If the encoding cannot be found, an
+ * identified by name by {@code charsetName}. If the encoding cannot be found, an
* UnsupportedEncodingException error is thrown.
*
* @param in
* the InputStream from which to read characters.
- * @param enc
+ * @param charsetName
* identifies the character converter to use.
* @throws NullPointerException
- * if {@code enc} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} cannot be found.
+ * if the encoding specified by {@code charsetName} cannot be found.
*/
- public InputStreamReader(InputStream in, final String enc)
+ public InputStreamReader(InputStream in, final String charsetName)
throws UnsupportedEncodingException {
super(in);
- if (enc == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.in = in;
try {
- decoder = Charset.forName(enc).newDecoder().onMalformedInput(
+ decoder = Charset.forName(charsetName).newDecoder().onMalformedInput(
CodingErrorAction.REPLACE).onUnmappableCharacter(
CodingErrorAction.REPLACE);
} catch (IllegalArgumentException e) {
throw (UnsupportedEncodingException)
- new UnsupportedEncodingException(enc).initCause(e);
+ new UnsupportedEncodingException(charsetName).initCause(e);
}
bytes.limit(0);
}
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 4541f1b..0476901 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -23,8 +23,8 @@ import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -1089,8 +1089,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec
for (ObjectStreamField fieldDesc : fields) {
Field field = classDesc.getReflectionField(fieldDesc);
- // We may not have been able to find the field, but we still need to read the value
- // and do the other checking, so there's no null check on 'field' here.
+ if (field != null && Modifier.isTransient(field.getModifiers())) {
+ field = null; // No setting transient fields! (http://b/4471249)
+ }
+ // We may not have been able to find the field, or it may be transient, but we still
+ // need to read the value and do the other checking...
try {
Class<?> type = fieldDesc.getTypeInternal();
if (type == byte.class) {
@@ -2341,7 +2344,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec
public int skipBytes(int length) throws IOException {
// To be used with available. Ok to call if reading primitive buffer
if (input == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source stream is null");
}
int offset = 0;
diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java
index e87fcd4..a28489a 100644
--- a/luni/src/main/java/java/io/ObjectStreamClass.java
+++ b/luni/src/main/java/java/io/ObjectStreamClass.java
@@ -481,16 +481,14 @@ public class ObjectStreamClass implements Serializable {
Field field = fields[i];
int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK;
- boolean skip = Modifier.isPrivate(modifiers)
- && (Modifier.isTransient(modifiers) || Modifier
- .isStatic(modifiers));
+ boolean skip = Modifier.isPrivate(modifiers) &&
+ (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers));
if (!skip) {
// write name, modifier & "descriptor" of all but private
// static and private transient
output.writeUTF(field.getName());
output.writeInt(modifiers);
- output
- .writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
+ output.writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
}
}
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index db450e0..78a6903 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -58,8 +58,10 @@ public class ObjectStreamField implements Comparable<Object> {
* if {@code name} or {@code cl} is {@code null}.
*/
public ObjectStreamField(String name, Class<?> cl) {
- if (name == null || cl == null) {
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ } else if (cl == null) {
+ throw new NullPointerException("cl == null");
}
this.name = name;
this.type = new WeakReference<Class<?>>(cl);
@@ -81,8 +83,10 @@ public class ObjectStreamField implements Comparable<Object> {
* @see ObjectOutputStream#writeUnshared(Object)
*/
public ObjectStreamField(String name, Class<?> cl, boolean unshared) {
- if (name == null || cl == null) {
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ } else if (cl == null) {
+ throw new NullPointerException("cl == null");
}
this.name = name;
this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
@@ -100,7 +104,7 @@ public class ObjectStreamField implements Comparable<Object> {
*/
ObjectStreamField(String signature, String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
this.name = name;
this.typeString = signature.replace('.', '/').intern();
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index 86b62fc..5dffdfe 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -57,30 +57,30 @@ public class OutputStreamWriter extends Writer {
/**
* Constructs a new OutputStreamWriter using {@code out} as the target
- * stream to write converted characters to and {@code enc} as the character
+ * stream to write converted characters to and {@code charsetName} as the character
* encoding. If the encoding cannot be found, an
* UnsupportedEncodingException error is thrown.
*
* @param out
* the target stream to write converted bytes to.
- * @param enc
+ * @param charsetName
* the string describing the desired character encoding.
* @throws NullPointerException
- * if {@code enc} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} cannot be found.
+ * if the encoding specified by {@code charsetName} cannot be found.
*/
- public OutputStreamWriter(OutputStream out, final String enc)
+ public OutputStreamWriter(OutputStream out, final String charsetName)
throws UnsupportedEncodingException {
super(out);
- if (enc == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.out = out;
try {
- encoder = Charset.forName(enc).newEncoder();
+ encoder = Charset.forName(charsetName).newEncoder();
} catch (Exception e) {
- throw new UnsupportedEncodingException(enc);
+ throw new UnsupportedEncodingException(charsetName);
}
encoder.onMalformedInput(CodingErrorAction.REPLACE);
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
@@ -106,19 +106,19 @@ public class OutputStreamWriter extends Writer {
/**
* Constructs a new OutputStreamWriter using {@code out} as the target
- * stream to write converted characters to and {@code enc} as the character
+ * stream to write converted characters to and {@code charsetEncoder} as the character
* encoder.
*
* @param out
* the target stream to write converted bytes to.
- * @param enc
+ * @param charsetEncoder
* the character encoder used for character conversion.
*/
- public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {
+ public OutputStreamWriter(OutputStream out, CharsetEncoder charsetEncoder) {
super(out);
- enc.charset();
+ charsetEncoder.charset();
this.out = out;
- encoder = enc;
+ encoder = charsetEncoder;
}
/**
diff --git a/luni/src/main/java/java/io/PipedOutputStream.java b/luni/src/main/java/java/io/PipedOutputStream.java
index a674bb3..1b139e9 100644
--- a/luni/src/main/java/java/io/PipedOutputStream.java
+++ b/luni/src/main/java/java/io/PipedOutputStream.java
@@ -81,7 +81,7 @@ public class PipedOutputStream extends OutputStream {
*/
public void connect(PipedInputStream stream) throws IOException {
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
synchronized (stream) {
if (this.target != null) {
diff --git a/luni/src/main/java/java/io/PipedWriter.java b/luni/src/main/java/java/io/PipedWriter.java
index ece899a..ad8974b 100644
--- a/luni/src/main/java/java/io/PipedWriter.java
+++ b/luni/src/main/java/java/io/PipedWriter.java
@@ -85,7 +85,7 @@ public class PipedWriter extends Writer {
*/
public void connect(PipedReader reader) throws IOException {
if (reader == null) {
- throw new NullPointerException();
+ throw new NullPointerException("reader == null");
}
synchronized (reader) {
if (this.destination != null) {
diff --git a/luni/src/main/java/java/io/PrintStream.java b/luni/src/main/java/java/io/PrintStream.java
index ba48b04..18f2310 100644
--- a/luni/src/main/java/java/io/PrintStream.java
+++ b/luni/src/main/java/java/io/PrintStream.java
@@ -59,7 +59,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close
public PrintStream(OutputStream out) {
super(out);
if (out == null) {
- throw new NullPointerException();
+ throw new NullPointerException("out == null");
}
}
@@ -80,14 +80,14 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close
public PrintStream(OutputStream out, boolean autoFlush) {
super(out);
if (out == null) {
- throw new NullPointerException();
+ throw new NullPointerException("out == null");
}
this.autoFlush = autoFlush;
}
/**
* Constructs a new {@code PrintStream} with {@code out} as its target
- * stream and using the character encoding {@code enc} while writing. The
+ * stream and using the character encoding {@code charsetName} while writing. The
* parameter {@code autoFlush} determines if the print stream automatically
* flushes its contents to the target stream when a newline is encountered.
*
@@ -96,28 +96,30 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close
* @param autoFlush
* indicates whether or not to flush contents upon encountering a
* newline sequence.
- * @param enc
+ * @param charsetName
* the non-null string describing the desired character encoding.
* @throws NullPointerException
- * if {@code out} or {@code enc} are {@code null}.
+ * if {@code out} or {@code charsetName} are {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(OutputStream out, boolean autoFlush, String enc)
+ public PrintStream(OutputStream out, boolean autoFlush, String charsetName)
throws UnsupportedEncodingException {
super(out);
- if (out == null || enc == null) {
- throw new NullPointerException();
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ } else if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.autoFlush = autoFlush;
try {
- if (!Charset.isSupported(enc)) {
- throw new UnsupportedEncodingException(enc);
+ if (!Charset.isSupported(charsetName)) {
+ throw new UnsupportedEncodingException(charsetName);
}
} catch (IllegalCharsetNameException e) {
- throw new UnsupportedEncodingException(enc);
+ throw new UnsupportedEncodingException(charsetName);
}
- encoding = enc;
+ encoding = charsetName;
}
/**
@@ -136,30 +138,30 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close
/**
* Constructs a new {@code PrintStream} with {@code file} as its target. The
- * character set named {@code csn} is used for character encoding.
+ * character set named {@code charsetName} is used for character encoding.
*
* @param file
* the target file. If the file already exists, its contents are
* removed, otherwise a new file is created.
- * @param csn
+ * @param charsetName
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
- * if {@code csn} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code csn} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(File file, String csn) throws FileNotFoundException,
+ public PrintStream(File file, String charsetName) throws FileNotFoundException,
UnsupportedEncodingException {
super(new FileOutputStream(file));
- if (csn == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
- if (!Charset.isSupported(csn)) {
- throw new UnsupportedEncodingException(csn);
+ if (!Charset.isSupported(charsetName)) {
+ throw new UnsupportedEncodingException(charsetName);
}
- encoding = csn;
+ encoding = charsetName;
}
/**
@@ -179,24 +181,24 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close
/**
* Constructs a new {@code PrintStream} with the file identified by
- * {@code fileName} as its target. The character set named {@code csn} is
+ * {@code fileName} as its target. The character set named {@code charsetName} is
* used for character encoding.
*
* @param fileName
* the target file's name. If the file already exists, its
* contents are removed, otherwise a new file is created.
- * @param csn
+ * @param charsetName
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
- * if {@code csn} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code csn} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(String fileName, String csn)
+ public PrintStream(String fileName, String charsetName)
throws FileNotFoundException, UnsupportedEncodingException {
- this(new File(fileName), csn);
+ this(new File(fileName), charsetName);
}
/**
diff --git a/luni/src/main/java/java/io/Reader.java b/luni/src/main/java/java/io/Reader.java
index 310a57c..e947d08 100644
--- a/luni/src/main/java/java/io/Reader.java
+++ b/luni/src/main/java/java/io/Reader.java
@@ -61,7 +61,7 @@ public abstract class Reader implements Readable, Closeable {
*/
protected Reader(Object lock) {
if (lock == null) {
- throw new NullPointerException();
+ throw new NullPointerException("lock == null");
}
this.lock = lock;
}
diff --git a/luni/src/main/java/java/io/SequenceInputStream.java b/luni/src/main/java/java/io/SequenceInputStream.java
index 9ae1901..8333834 100644
--- a/luni/src/main/java/java/io/SequenceInputStream.java
+++ b/luni/src/main/java/java/io/SequenceInputStream.java
@@ -50,7 +50,7 @@ public class SequenceInputStream extends InputStream {
*/
public SequenceInputStream(InputStream s1, InputStream s2) {
if (s1 == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s1 == null");
}
Vector<InputStream> inVector = new Vector<InputStream>(1);
inVector.addElement(s2);
@@ -73,7 +73,7 @@ public class SequenceInputStream extends InputStream {
if (e.hasMoreElements()) {
in = e.nextElement();
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("element is null");
}
}
}
@@ -112,7 +112,7 @@ public class SequenceInputStream extends InputStream {
if (e.hasMoreElements()) {
in = e.nextElement();
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("element is null");
}
} else {
in = null;
diff --git a/luni/src/main/java/java/io/StreamTokenizer.java b/luni/src/main/java/java/io/StreamTokenizer.java
index 0522be6..a16dc4b 100644
--- a/luni/src/main/java/java/io/StreamTokenizer.java
+++ b/luni/src/main/java/java/io/StreamTokenizer.java
@@ -167,7 +167,7 @@ public class StreamTokenizer {
public StreamTokenizer(InputStream is) {
this();
if (is == null) {
- throw new NullPointerException();
+ throw new NullPointerException("is == null");
}
inStream = is;
}
@@ -194,7 +194,7 @@ public class StreamTokenizer {
public StreamTokenizer(Reader r) {
this();
if (r == null) {
- throw new NullPointerException();
+ throw new NullPointerException("r == null");
}
inReader = r;
}
diff --git a/luni/src/main/java/java/io/StringBufferInputStream.java b/luni/src/main/java/java/io/StringBufferInputStream.java
index 1fada57..1768abe 100644
--- a/luni/src/main/java/java/io/StringBufferInputStream.java
+++ b/luni/src/main/java/java/io/StringBufferInputStream.java
@@ -54,7 +54,7 @@ public class StringBufferInputStream extends InputStream {
*/
public StringBufferInputStream(String str) {
if (str == null) {
- throw new NullPointerException();
+ throw new NullPointerException("str == null");
}
buffer = str;
count = str.length();
diff --git a/luni/src/main/java/java/io/Writer.java b/luni/src/main/java/java/io/Writer.java
index 2e28b80..33d7604 100644
--- a/luni/src/main/java/java/io/Writer.java
+++ b/luni/src/main/java/java/io/Writer.java
@@ -59,7 +59,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable {
*/
protected Writer(Object lock) {
if (lock == null) {
- throw new NullPointerException();
+ throw new NullPointerException("lock == null");
}
this.lock = lock;
}
diff --git a/luni/src/main/java/java/lang/AbstractStringBuilder.java b/luni/src/main/java/java/lang/AbstractStringBuilder.java
index baab47d..c3107f2 100644
--- a/luni/src/main/java/java/lang/AbstractStringBuilder.java
+++ b/luni/src/main/java/java/lang/AbstractStringBuilder.java
@@ -77,7 +77,7 @@ abstract class AbstractStringBuilder {
AbstractStringBuilder(int capacity) {
if (capacity < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(capacity));
}
value = new char[capacity];
}
@@ -437,7 +437,7 @@ abstract class AbstractStringBuilder {
}
if (start == end) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
insert0(start, string);
return;
diff --git a/luni/src/main/java/java/lang/Character.java b/luni/src/main/java/java/lang/Character.java
index 1a41ec2..cf0ab84 100644
--- a/luni/src/main/java/java/lang/Character.java
+++ b/luni/src/main/java/java/lang/Character.java
@@ -532,7 +532,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
protected Subset(String string) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
name = string;
}
@@ -1502,7 +1502,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static UnicodeBlock forName(String blockName) {
if (blockName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("blockName == null");
}
int block = forNameImpl(blockName);
if (block == -1) {
@@ -1798,7 +1798,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int codePointAt(CharSequence seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 0 || index >= len) {
@@ -1840,7 +1840,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int codePointAt(char[] seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index < 0 || index >= len) {
@@ -1923,7 +1923,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int codePointBefore(CharSequence seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 1 || index > len) {
@@ -1965,7 +1965,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int codePointBefore(char[] seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index < 1 || index > len) {
@@ -2012,7 +2012,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int codePointBefore(char[] seq, int index, int start) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index <= start || index > len || start < 0 || start >= len) {
@@ -2055,7 +2055,7 @@ public final class Character implements Serializable, Comparable<Character> {
public static int toChars(int codePoint, char[] dst, int dstIndex) {
checkValidCodePoint(codePoint);
if (dst == null) {
- throw new NullPointerException();
+ throw new NullPointerException("dst == null");
}
if (dstIndex < 0 || dstIndex >= dst.length) {
throw new IndexOutOfBoundsException();
@@ -2126,7 +2126,7 @@ public final class Character implements Serializable, Comparable<Character> {
public static int codePointCount(CharSequence seq, int beginIndex,
int endIndex) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (beginIndex < 0 || endIndex > len || beginIndex > endIndex) {
@@ -2215,7 +2215,7 @@ public final class Character implements Serializable, Comparable<Character> {
*/
public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 0 || index > len) {
diff --git a/luni/src/main/java/java/lang/ClassLoader.java b/luni/src/main/java/java/lang/ClassLoader.java
index 0cdc448..c99d57c 100644
--- a/luni/src/main/java/java/lang/ClassLoader.java
+++ b/luni/src/main/java/java/lang/ClassLoader.java
@@ -195,7 +195,7 @@ public abstract class ClassLoader {
*/
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
if (parentLoader == null && !nullAllowed) {
- throw new NullPointerException("Parent ClassLoader may not be null");
+ throw new NullPointerException("parentLoader == null && !nullAllowed");
}
parent = parentLoader;
}
diff --git a/luni/src/main/java/java/lang/Daemons.java b/luni/src/main/java/java/lang/Daemons.java
index a7d0964..78a4152 100644
--- a/luni/src/main/java/java/lang/Daemons.java
+++ b/luni/src/main/java/java/lang/Daemons.java
@@ -31,8 +31,9 @@ import libcore.util.EmptyArray;
* @hide
*/
public final class Daemons {
- private static final int NANOS_PER_MILLI = 1000000;
- private static final long MAX_FINALIZE_MILLIS = 10L * 1000L; // 10 seconds
+ private static final int NANOS_PER_MILLI = 1000 * 1000;
+ private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+ private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
public static void start() {
ReferenceQueueDaemon.INSTANCE.start();
@@ -203,41 +204,78 @@ public final class Daemons {
@Override public void run() {
while (isRunning()) {
- try {
- Object object = FinalizerDaemon.INSTANCE.finalizingObject;
- long startedNanos = FinalizerDaemon.INSTANCE.finalizingStartedNanos;
-
- if (object == null) {
- synchronized (this) {
- // wait until something is being finalized
- // http://code.google.com/p/android/issues/detail?id=22778
- wait();
- continue;
- }
- }
+ Object object = waitForObject();
+ if (object == null) {
+ // We have been interrupted, need to see if this daemon has been stopped.
+ continue;
+ }
+ boolean finalized = waitForFinalization(object);
+ if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) {
+ finalizerTimedOut(object);
+ break;
+ }
+ }
+ }
- long elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
- long sleepMillis = MAX_FINALIZE_MILLIS - elapsedMillis;
- if (sleepMillis > 0) {
- Thread.sleep(sleepMillis);
- elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
+ private Object waitForObject() {
+ while (true) {
+ Object object = FinalizerDaemon.INSTANCE.finalizingObject;
+ if (object != null) {
+ return object;
+ }
+ synchronized (this) {
+ // wait until something is ready to be finalized
+ // http://code.google.com/p/android/issues/detail?id=22778
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // Daemon.stop may have interrupted us.
+ return null;
}
+ }
+ }
+ }
- if (object != FinalizerDaemon.INSTANCE.finalizingObject
- || VMRuntime.getRuntime().isDebuggerActive()) {
- continue;
+ private void sleepFor(long startNanos, long durationNanos) {
+ while (true) {
+ long elapsedNanos = System.nanoTime() - startNanos;
+ long sleepNanos = durationNanos - elapsedNanos;
+ long sleepMills = sleepNanos / NANOS_PER_MILLI;
+ if (sleepMills <= 0) {
+ return;
+ }
+ try {
+ Thread.sleep(sleepMills);
+ } catch (InterruptedException e) {
+ if (!isRunning()) {
+ return;
}
-
- // The current object has exceeded the finalization deadline; abort!
- Exception syntheticException = new TimeoutException();
- syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
- System.logE(object.getClass().getName() + ".finalize() timed out after "
- + elapsedMillis + " ms; limit is " + MAX_FINALIZE_MILLIS + " ms",
- syntheticException);
- System.exit(2);
- } catch (InterruptedException ignored) {
}
}
}
+
+ private boolean waitForFinalization(Object object) {
+ sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS);
+ return object != FinalizerDaemon.INSTANCE.finalizingObject;
+ }
+
+ private static void finalizerTimedOut(Object object) {
+ // The current object has exceeded the finalization deadline; abort!
+ String message = object.getClass().getName() + ".finalize() timed out after "
+ + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
+ Exception syntheticException = new TimeoutException(message);
+ // We use the stack from where finalize() was running to show where it was stuck.
+ syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
+ Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
+ if (h == null) {
+ // If we have no handler, log and exit.
+ System.logE(message, syntheticException);
+ System.exit(2);
+ }
+ // Otherwise call the handler to do crash reporting.
+ // We don't just throw because we're not the thread that
+ // timed out; we're the thread that detected it.
+ h.uncaughtException(Thread.currentThread(), syntheticException);
+ }
}
}
diff --git a/luni/src/main/java/java/lang/Enum.java b/luni/src/main/java/java/lang/Enum.java
index 391670c..7a0f514 100644
--- a/luni/src/main/java/java/lang/Enum.java
+++ b/luni/src/main/java/java/lang/Enum.java
@@ -34,6 +34,9 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl
private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache
= new BasicLruCache<Class<? extends Enum>, Object[]>(64) {
@Override protected Object[] create(Class<? extends Enum> enumType) {
+ if (!enumType.isEnum()) {
+ return null;
+ }
Method method = (Method) Class.getDeclaredConstructorOrMethod(
enumType, "values", EmptyArray.CLASS);
try {
@@ -178,13 +181,16 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl
* have a constant value called {@code name}.
*/
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
- if (enumType == null || name == null) {
- throw new NullPointerException("enumType == null || name == null");
+ if (enumType == null) {
+ throw new NullPointerException("enumType == null");
+ } else if (name == null) {
+ throw new NullPointerException("name == null");
}
- if (!enumType.isEnum()) {
+ T[] values = getSharedConstants(enumType);
+ if (values == null) {
throw new IllegalArgumentException(enumType + " is not an enum type");
}
- for (T value : getSharedConstants(enumType)) {
+ for (T value : values) {
if (name.equals(value.name())) {
return value;
}
diff --git a/luni/src/main/java/java/lang/Object.java b/luni/src/main/java/java/lang/Object.java
index 7f4b490..4bca034 100644
--- a/luni/src/main/java/java/lang/Object.java
+++ b/luni/src/main/java/java/lang/Object.java
@@ -361,7 +361,7 @@ public class Object {
* @see java.lang.Thread
*/
public final void wait() throws InterruptedException {
- wait(0 ,0);
+ wait(0, 0);
}
/**
diff --git a/luni/src/main/java/java/lang/ProcessBuilder.java b/luni/src/main/java/java/lang/ProcessBuilder.java
index 5b7efdc..57e21b6 100644
--- a/luni/src/main/java/java/lang/ProcessBuilder.java
+++ b/luni/src/main/java/java/lang/ProcessBuilder.java
@@ -59,7 +59,7 @@ public final class ProcessBuilder {
*/
public ProcessBuilder(List<String> command) {
if (command == null) {
- throw new NullPointerException();
+ throw new NullPointerException("command == null");
}
this.command = command;
@@ -102,7 +102,7 @@ public final class ProcessBuilder {
*/
public ProcessBuilder command(List<String> command) {
if (command == null) {
- throw new NullPointerException();
+ throw new NullPointerException("command == null");
}
this.command = command;
return this;
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index 1e820a9..28314b7 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -168,10 +168,10 @@ final class ProcessManager {
boolean redirectErrorStream) throws IOException {
// Make sure we throw the same exceptions as the RI.
if (taintedCommand == null) {
- throw new NullPointerException();
+ throw new NullPointerException("taintedCommand == null");
}
if (taintedCommand.length == 0) {
- throw new IndexOutOfBoundsException();
+ throw new IndexOutOfBoundsException("taintedCommand.length == 0");
}
// Handle security and safety by copying mutable inputs and checking them.
@@ -179,16 +179,16 @@ final class ProcessManager {
String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;
// Check we're not passing null Strings to the native exec.
- for (String arg : command) {
- if (arg == null) {
- throw new NullPointerException();
+ for (int i = 0; i < command.length; i++) {
+ if (command[i] == null) {
+ throw new NullPointerException("taintedCommand[" + i + "] == null");
}
}
// The environment is allowed to be null or empty, but no element may be null.
if (environment != null) {
- for (String env : environment) {
- if (env == null) {
- throw new NullPointerException();
+ for (int i = 0; i < environment.length; i++) {
+ if (environment[i] == null) {
+ throw new NullPointerException("taintedEnvironment[" + i + "] == null");
}
}
}
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index 320f157..a2debfd 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -224,9 +224,9 @@ public class Runtime {
public Process exec(String prog, String[] envp, File directory) throws java.io.IOException {
// Sanity checks
if (prog == null) {
- throw new NullPointerException();
- } else if (prog.length() == 0) {
- throw new IllegalArgumentException();
+ throw new NullPointerException("prog == null");
+ } else if (prog.isEmpty()) {
+ throw new IllegalArgumentException("prog is empty");
}
// Break down into tokens, as described in Java docs
@@ -331,11 +331,11 @@ public class Runtime {
/*
* Loads and links a library without security checks.
*/
- void load(String filename, ClassLoader loader) {
- if (filename == null) {
- throw new NullPointerException("library path was null.");
+ void load(String pathName, ClassLoader loader) {
+ if (pathName == null) {
+ throw new NullPointerException("pathName == null");
}
- String error = nativeLoad(filename, loader);
+ String error = nativeLoad(pathName, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
@@ -362,8 +362,9 @@ public class Runtime {
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
- throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " +
- "findLibrary returned null");
+ throw new UnsatisfiedLinkError("Couldn't load " + libraryName
+ + " from loader " + loader
+ + ": findLibrary returned null");
}
String error = nativeLoad(filename, loader);
if (error != null) {
@@ -537,7 +538,7 @@ public class Runtime {
public void addShutdownHook(Thread hook) {
// Sanity checks
if (hook == null) {
- throw new NullPointerException("Hook may not be null.");
+ throw new NullPointerException("hook == null");
}
if (shuttingDown) {
@@ -570,7 +571,7 @@ public class Runtime {
public boolean removeShutdownHook(Thread hook) {
// Sanity checks
if (hook == null) {
- throw new NullPointerException("Hook may not be null.");
+ throw new NullPointerException("hook == null");
}
if (shuttingDown) {
diff --git a/luni/src/main/java/java/lang/StackTraceElement.java b/luni/src/main/java/java/lang/StackTraceElement.java
index b83120c..a59935a 100644
--- a/luni/src/main/java/java/lang/StackTraceElement.java
+++ b/luni/src/main/java/java/lang/StackTraceElement.java
@@ -58,8 +58,10 @@ public final class StackTraceElement implements Serializable {
* if {@code cls} or {@code method} is {@code null}.
*/
public StackTraceElement(String cls, String method, String file, int line) {
- if (cls == null || method == null) {
- throw new NullPointerException();
+ if (cls == null) {
+ throw new NullPointerException("cls == null");
+ } else if (method == null) {
+ throw new NullPointerException("method == null");
}
declaringClass = cls;
methodName = method;
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index efd4210..f3aeb64 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -168,17 +168,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque
* if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}.
*/
public String(byte[] data, int offset, int byteCount) {
- if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
- throw failedBoundsCheck(data.length, offset, byteCount);
- }
- CharBuffer cb = Charset.defaultCharset().decode(ByteBuffer.wrap(data, offset, byteCount));
- this.count = cb.length();
- this.offset = 0;
- if (count > 0) {
- value = cb.array();
- } else {
- value = EmptyArray.CHAR;
- }
+ this(data, offset, byteCount, Charset.defaultCharset());
}
/**
@@ -524,7 +514,7 @@ outer:
*/
public String(int[] codePoints, int offset, int count) {
if (codePoints == null) {
- throw new NullPointerException();
+ throw new NullPointerException("codePoints == null");
}
if ((offset | count) < 0 || count > codePoints.length - offset) {
throw failedBoundsCheck(codePoints.length, offset, count);
@@ -1232,7 +1222,7 @@ outer:
*/
public boolean regionMatches(int thisStart, String string, int start, int length) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
if (start < 0 || string.count - start < length) {
return false;
@@ -1729,7 +1719,7 @@ outer:
*/
public boolean contentEquals(CharSequence cs) {
if (cs == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cs == null");
}
int len = cs.length();
@@ -1922,7 +1912,7 @@ outer:
*/
public boolean contains(CharSequence cs) {
if (cs == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cs == null");
}
return indexOf(cs.toString()) >= 0;
}
@@ -1991,7 +1981,7 @@ outer:
*/
public static String format(Locale locale, String format, Object... args) {
if (format == null) {
- throw new NullPointerException("null format argument");
+ throw new NullPointerException("format == null");
}
int bufferSize = format.length() + (args == null ? 0 : args.length * 10);
Formatter f = new Formatter(new StringBuilder(bufferSize), locale);
diff --git a/luni/src/main/java/java/lang/StringBuilder.java b/luni/src/main/java/java/lang/StringBuilder.java
index d886100..a944e68 100644
--- a/luni/src/main/java/java/lang/StringBuilder.java
+++ b/luni/src/main/java/java/lang/StringBuilder.java
@@ -624,7 +624,7 @@ public final class StringBuilder extends AbstractStringBuilder implements
* the inclusive begin index.
* @param end
* the exclusive end index.
- * @param str
+ * @param string
* the replacement string.
* @return this builder.
* @throws StringIndexOutOfBoundsException
@@ -633,8 +633,8 @@ public final class StringBuilder extends AbstractStringBuilder implements
* @throws NullPointerException
* if {@code str} is {@code null}.
*/
- public StringBuilder replace(int start, int end, String str) {
- replace0(start, end, str);
+ public StringBuilder replace(int start, int end, String string) {
+ replace0(start, end, string);
return this;
}
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index 24ebf6b..df84c61 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -34,6 +34,7 @@ package java.lang;
import dalvik.system.VMRuntime;
import dalvik.system.VMStack;
+import java.io.BufferedInputStream;
import java.io.Console;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -83,10 +84,9 @@ public final class System {
private static Properties systemProperties;
static {
- // TODO: all three streams are buffered in Harmony.
err = new PrintStream(new FileOutputStream(FileDescriptor.err));
out = new PrintStream(new FileOutputStream(FileDescriptor.out));
- in = new FileInputStream(FileDescriptor.in);
+ in = new BufferedInputStream(new FileInputStream(FileDescriptor.in));
lineSeparator = System.getProperty("line.separator");
}
@@ -455,7 +455,7 @@ public final class System {
*/
public static String clearProperty(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.isEmpty()) {
throw new IllegalArgumentException();
@@ -679,7 +679,7 @@ public final class System {
private String toNonNullString(Object o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
return (String) o;
}
diff --git a/luni/src/main/java/java/lang/Thread.java b/luni/src/main/java/java/lang/Thread.java
index a61f669..210b90c 100644
--- a/luni/src/main/java/java/lang/Thread.java
+++ b/luni/src/main/java/java/lang/Thread.java
@@ -232,7 +232,7 @@ public class Thread implements Runnable {
*/
public Thread(Runnable runnable, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(null, runnable, threadName, 0);
@@ -252,7 +252,7 @@ public class Thread implements Runnable {
*/
public Thread(String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(null, null, threadName, 0);
@@ -296,7 +296,7 @@ public class Thread implements Runnable {
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, runnable, threadName, 0);
@@ -317,7 +317,7 @@ public class Thread implements Runnable {
*/
public Thread(ThreadGroup group, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, null, threadName, 0);
@@ -346,7 +346,7 @@ public class Thread implements Runnable {
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, runnable, threadName, stackSize);
}
@@ -943,7 +943,7 @@ public class Thread implements Runnable {
*/
public final void setName(String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
name = threadName;
diff --git a/luni/src/main/java/java/lang/Throwable.java b/luni/src/main/java/java/lang/Throwable.java
index b561832..b20b882 100644
--- a/luni/src/main/java/java/lang/Throwable.java
+++ b/luni/src/main/java/java/lang/Throwable.java
@@ -23,6 +23,7 @@ import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import libcore.util.EmptyArray;
@@ -63,13 +64,13 @@ public class Throwable implements java.io.Serializable {
* Throwables suppressed by this throwable. Null when suppressed exceptions
* are disabled.
*/
- private List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
+ private List<Throwable> suppressedExceptions = Collections.emptyList();
/**
* An intermediate representation of the stack trace. This field may
* be accessed by the VM; do not rename.
*/
- private volatile Object stackState;
+ private transient volatile Object stackState;
/**
* A fully-expanded representation of the stack trace.
@@ -218,9 +219,9 @@ public class Throwable implements java.io.Serializable {
*/
public void setStackTrace(StackTraceElement[] trace) {
StackTraceElement[] newTrace = trace.clone();
- for (StackTraceElement element : newTrace) {
- if (element == null) {
- throw new NullPointerException();
+ for (int i = 0; i < newTrace.length; i++) {
+ if (newTrace[i] == null) {
+ throw new NullPointerException("trace[" + i + "] == null");
}
}
stackTrace = newTrace;
@@ -412,12 +413,17 @@ public class Throwable implements java.io.Serializable {
*/
public final void addSuppressed(Throwable throwable) {
if (throwable == this) {
- throw new IllegalArgumentException("suppressed == this");
+ throw new IllegalArgumentException("throwable == this");
}
if (throwable == null) {
- throw new NullPointerException("suppressed == null");
+ throw new NullPointerException("throwable == null");
}
if (suppressedExceptions != null) {
+ // suppressed exceptions are enabled
+ if (suppressedExceptions.isEmpty()) {
+ // ensure we have somewhere to place suppressed exceptions
+ suppressedExceptions = new ArrayList<Throwable>(1);
+ }
suppressedExceptions.add(throwable);
}
}
@@ -429,7 +435,7 @@ public class Throwable implements java.io.Serializable {
* @hide 1.7
*/
public final Throwable[] getSuppressed() {
- return (suppressedExceptions != null)
+ return (suppressedExceptions != null && !suppressedExceptions.isEmpty())
? suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()])
: EmptyArray.THROWABLE;
}
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java
index aadf1f6..14eaae4 100644
--- a/luni/src/main/java/java/lang/ref/FinalizerReference.java
+++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java
@@ -20,33 +20,39 @@ package java.lang.ref;
* @hide
*/
public final class FinalizerReference<T> extends Reference<T> {
+ // This queue contains those objects eligible for finalization.
public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
- private static FinalizerReference head = null;
+ // Guards the list (not the queue).
+ private static final Object LIST_LOCK = new Object();
- private T zombie;
+ // This list contains a FinalizerReference for every finalizable object in the heap.
+ // Objects in this list may or may not be eligible for finalization yet.
+ private static FinalizerReference<?> head = null;
- private FinalizerReference prev;
+ // The links used to construct the list.
+ private FinalizerReference<?> prev;
+ private FinalizerReference<?> next;
- private FinalizerReference next;
+ // When the GC wants something finalized, it moves it from the 'referent' field to
+ // the 'zombie' field instead.
+ private T zombie;
public FinalizerReference(T r, ReferenceQueue<? super T> q) {
super(r, q);
}
- @Override
- public T get() {
+ @Override public T get() {
return zombie;
}
- @Override
- public void clear() {
+ @Override public void clear() {
zombie = null;
}
- static void add(Object referent) {
+ public static void add(Object referent) {
FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
- synchronized (FinalizerReference.class) {
+ synchronized (LIST_LOCK) {
reference.prev = null;
reference.next = head;
if (head != null) {
@@ -56,10 +62,10 @@ public final class FinalizerReference<T> extends Reference<T> {
}
}
- public static void remove(FinalizerReference reference) {
- synchronized (FinalizerReference.class) {
- FinalizerReference next = reference.next;
- FinalizerReference prev = reference.prev;
+ public static void remove(FinalizerReference<?> reference) {
+ synchronized (LIST_LOCK) {
+ FinalizerReference<?> next = reference.next;
+ FinalizerReference<?> prev = reference.prev;
reference.next = null;
reference.prev = null;
if (prev != null) {
@@ -74,30 +80,50 @@ public final class FinalizerReference<T> extends Reference<T> {
}
/**
- * Returns once all currently-enqueued references have been finalized.
+ * Waits for all currently-enqueued references to be finalized.
*/
public static void finalizeAllEnqueued() throws InterruptedException {
Sentinel sentinel = new Sentinel();
- FinalizerReference<Object> reference = new FinalizerReference<Object>(null, queue);
- reference.zombie = sentinel;
- reference.enqueueInternal();
+ enqueueSentinelReference(sentinel);
sentinel.awaitFinalization();
}
+ private static void enqueueSentinelReference(Sentinel sentinel) {
+ synchronized (LIST_LOCK) {
+ // When a finalizable object is allocated, a FinalizerReference is added to the list.
+ // We search the list for that FinalizerReference (it should be at or near the head),
+ // and then put it on the queue so that it can be finalized.
+ for (FinalizerReference<?> r = head; r != null; r = r.next) {
+ if (r.referent == sentinel) {
+ FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r;
+ sentinelReference.referent = null;
+ sentinelReference.zombie = sentinel;
+ sentinelReference.enqueueInternal();
+ return;
+ }
+ }
+ }
+ // We just created a finalizable object and still hold a reference to it.
+ // It must be on the list.
+ throw new AssertionError("newly-created live Sentinel not on list!");
+ }
+
/**
* A marker object that we can immediately enqueue. When this object's
* finalize() method is called, we know all previously-enqueued finalizable
* references have been finalized.
- *
- * <p>Each instance of this class will be finalized twice as it is enqueued
- * directly and by the garbage collector.
*/
private static class Sentinel {
boolean finalized = false;
+
@Override protected synchronized void finalize() throws Throwable {
+ if (finalized) {
+ throw new AssertionError();
+ }
finalized = true;
notifyAll();
}
+
synchronized void awaitFinalization() throws InterruptedException {
while (!finalized) {
wait();
diff --git a/luni/src/main/java/java/lang/ref/Reference.java b/luni/src/main/java/java/lang/ref/Reference.java
index 85fbb04..9cf49a7 100644
--- a/luni/src/main/java/java/lang/ref/Reference.java
+++ b/luni/src/main/java/java/lang/ref/Reference.java
@@ -55,8 +55,7 @@ public abstract class Reference<T> {
* VM requirement: this field <em>must</em> be called "queue"
* and be a java.lang.ref.ReferenceQueue.
*/
- @SuppressWarnings("unchecked")
- volatile ReferenceQueue queue;
+ volatile ReferenceQueue<? super T> queue;
/**
* Used internally by java.lang.ref.ReferenceQueue.
@@ -82,7 +81,7 @@ public abstract class Reference<T> {
Reference() {
}
- Reference(T r, ReferenceQueue q) {
+ Reference(T r, ReferenceQueue<? super T> q) {
referent = r;
queue = q;
}
diff --git a/luni/src/main/java/java/lang/ref/ReferenceQueue.java b/luni/src/main/java/java/lang/ref/ReferenceQueue.java
index 6c9b4d5..2b8089c 100644
--- a/luni/src/main/java/java/lang/ref/ReferenceQueue.java
+++ b/luni/src/main/java/java/lang/ref/ReferenceQueue.java
@@ -131,8 +131,6 @@ public class ReferenceQueue<T> {
*
* @param reference
* reference object to be enqueued.
- * @return boolean true if reference is enqueued. false if reference failed
- * to enqueue.
*/
synchronized void enqueue(Reference<? extends T> reference) {
if (head == null) {
@@ -145,7 +143,7 @@ public class ReferenceQueue<T> {
}
/** @hide */
- public static Reference unenqueued = null;
+ public static Reference<?> unenqueued = null;
static void add(Reference<?> list) {
synchronized (ReferenceQueue.class) {
diff --git a/luni/src/main/java/java/lang/reflect/GenericArrayType.java b/luni/src/main/java/java/lang/reflect/GenericArrayType.java
index 6344019..fc03f78 100644
--- a/luni/src/main/java/java/lang/reflect/GenericArrayType.java
+++ b/luni/src/main/java/java/lang/reflect/GenericArrayType.java
@@ -36,4 +36,4 @@ public interface GenericArrayType extends Type {
* instantiated for some reason
*/
Type getGenericComponentType();
-} \ No newline at end of file
+}
diff --git a/luni/src/main/java/java/lang/reflect/Proxy.java b/luni/src/main/java/java/lang/reflect/Proxy.java
index a24514d..3b10887 100644
--- a/luni/src/main/java/java/lang/reflect/Proxy.java
+++ b/luni/src/main/java/java/lang/reflect/Proxy.java
@@ -89,13 +89,13 @@ public class Proxy implements Serializable {
Class<?>... interfaces) throws IllegalArgumentException {
// check that interfaces are a valid array of visible interfaces
if (interfaces == null) {
- throw new NullPointerException();
+ throw new NullPointerException("interfaces == null");
}
String commonPackageName = null;
for (int i = 0, length = interfaces.length; i < length; i++) {
Class<?> next = interfaces[i];
if (next == null) {
- throw new NullPointerException();
+ throw new NullPointerException("interfaces[" + i + "] == null");
}
String name = next.getName();
if (!next.isInterface()) {
@@ -206,7 +206,7 @@ public class Proxy implements Serializable {
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
- throw new NullPointerException();
+ throw new NullPointerException("h == null");
}
try {
return getProxyClass(loader, interfaces).getConstructor(
@@ -241,7 +241,7 @@ public class Proxy implements Serializable {
*/
public static boolean isProxyClass(Class<?> cl) {
if (cl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cl == null");
}
synchronized (proxyCache) {
return proxyCache.containsKey(cl);
diff --git a/luni/src/main/java/java/math/BigDecimal.java b/luni/src/main/java/java/math/BigDecimal.java
index 3a5f3cd..335e3bc 100644
--- a/luni/src/main/java/java/math/BigDecimal.java
+++ b/luni/src/main/java/java/math/BigDecimal.java
@@ -276,7 +276,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial
long newScale; // the new scale
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
if ((last >= in.length) || (offset < 0) || (len <= 0) || (last < 0)) {
throw new NumberFormatException("Bad offset/length: offset=" + offset +
@@ -601,7 +601,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial
*/
public BigDecimal(BigInteger unscaledVal, int scale) {
if (unscaledVal == null) {
- throw new NullPointerException();
+ throw new NullPointerException("unscaledVal == null");
}
this.scale = scale;
setUnscaledValue(unscaledVal);
@@ -1059,7 +1059,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
// Let be: this = [u1,s1] and divisor = [u2,s2]
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
if (divisor.isZero()) {
throw new ArithmeticException("Division by zero");
@@ -1916,7 +1916,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial
*/
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
long diffScale = newScale - (long)scale;
// Let be: 'this' = [u,s]
diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java
index 1768676..614dbb4 100644
--- a/luni/src/main/java/java/math/BigInt.java
+++ b/luni/src/main/java/java/math/BigInt.java
@@ -153,7 +153,7 @@ final class BigInt {
*/
String checkString(String s, int base) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
// A valid big integer consists of an optional '-' or '+' followed by
// one or more digit characters appropriate to the given base,
diff --git a/luni/src/main/java/java/net/CookieStore.java b/luni/src/main/java/java/net/CookieStore.java
index d09b7e8..619d65c 100644
--- a/luni/src/main/java/java/net/CookieStore.java
+++ b/luni/src/main/java/java/net/CookieStore.java
@@ -100,4 +100,4 @@ public interface CookieStore {
* @return true if any cookies were removed as a result of this call.
*/
boolean removeAll();
-} \ No newline at end of file
+}
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index 2b468fa..c01f3af 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -244,7 +244,7 @@ public class DatagramSocket {
checkOpen();
ensureBound();
if (pack == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pack == null");
}
if (pendingConnectException != null) {
throw new SocketException("Pending connect failure", pendingConnectException);
@@ -295,7 +295,7 @@ public class DatagramSocket {
*/
public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
if (netInterface == null) {
- throw new NullPointerException("networkInterface == null");
+ throw new NullPointerException("netInterface == null");
}
try {
Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
@@ -374,7 +374,7 @@ public class DatagramSocket {
*/
protected DatagramSocket(DatagramSocketImpl socketImpl) {
if (socketImpl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("socketImpl == null");
}
impl = socketImpl;
}
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index e06b811..ad81f32 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -122,7 +122,9 @@ public final class NetworkInterface extends Object {
private static void collectIpv6Addresses(String interfaceName, int interfaceIndex,
List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException {
- // Format of /proc/net/if_inet6 (all numeric fields are implicit hex).
+ // Format of /proc/net/if_inet6.
+ // All numeric fields are implicit hex,
+ // but not necessarily two-digit (http://code.google.com/p/android/issues/detail?id=34022).
// 1. IPv6 address
// 2. interface index
// 3. prefix length
@@ -130,6 +132,7 @@ public final class NetworkInterface extends Object {
// 5. flags
// 6. interface name
// "00000000000000000000000000000001 01 80 10 80 lo"
+ // "fe800000000000000000000000000000 407 40 20 80 wlan0"
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader("/proc/net/if_inet6"));
@@ -139,13 +142,22 @@ public final class NetworkInterface extends Object {
if (!line.endsWith(suffix)) {
continue;
}
+
+ // Extract the IPv6 address.
byte[] addressBytes = new byte[16];
for (int i = 0; i < addressBytes.length; ++i) {
addressBytes[i] = (byte) Integer.parseInt(line.substring(2*i, 2*i + 2), 16);
}
- short prefixLength = Short.parseShort(line.substring(36, 38), 16);
- Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex);
+ // Extract the prefix length.
+ // Skip the IPv6 address and its trailing space.
+ int prefixLengthStart = 32 + 1;
+ // Skip the interface index and its trailing space.
+ prefixLengthStart = line.indexOf(' ', prefixLengthStart) + 1;
+ int prefixLengthEnd = line.indexOf(' ', prefixLengthStart);
+ short prefixLength = Short.parseShort(line.substring(prefixLengthStart, prefixLengthEnd), 16);
+
+ Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex);
addresses.add(inet6Address);
interfaceAddresses.add(new InterfaceAddress(inet6Address, prefixLength));
}
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 5d01469..3226527 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -216,6 +216,8 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
} catch (ErrnoException errnoException) {
throw new AssertionError(errnoException);
+ } catch (SocketException ignored) {
+ // Thrown if the socket has already been closed, but this method can't throw anything.
}
connectedPort = -1;
connectedAddress = null;
diff --git a/luni/src/main/java/java/net/URISyntaxException.java b/luni/src/main/java/java/net/URISyntaxException.java
index e08f444..957ea31 100644
--- a/luni/src/main/java/java/net/URISyntaxException.java
+++ b/luni/src/main/java/java/net/URISyntaxException.java
@@ -49,8 +49,10 @@ public class URISyntaxException extends Exception {
public URISyntaxException(String input, String reason, int index) {
super(reason);
- if (input == null || reason == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (reason == null) {
+ throw new NullPointerException("reason == null");
}
if (index < -1) {
@@ -76,8 +78,10 @@ public class URISyntaxException extends Exception {
public URISyntaxException(String input, String reason) {
super(reason);
- if (input == null || reason == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (reason == null) {
+ throw new NullPointerException("reason == null");
}
this.input = input;
diff --git a/luni/src/main/java/java/net/URLClassLoader.java b/luni/src/main/java/java/net/URLClassLoader.java
index 1c8bc43..efb7531 100644
--- a/luni/src/main/java/java/net/URLClassLoader.java
+++ b/luni/src/main/java/java/net/URLClassLoader.java
@@ -822,7 +822,7 @@ public class URLClassLoader extends SecureClassLoader {
while (!searchList.isEmpty()) {
URL nextCandidate = searchList.remove(0);
if (nextCandidate == null) {
- throw new NullPointerException("A URL is null");
+ throw new NullPointerException("nextCandidate == null");
}
if (!handlerMap.containsKey(nextCandidate)) {
URLHandler result;
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index c832bfb..18a264e 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -948,11 +948,12 @@ public abstract class URLConnection {
}
/**
- * Sets the maximum time to wait for a connect to complete before giving up.
+ * Sets the maximum time in milliseconds to wait while connecting.
* Connecting to a server will fail with a {@link SocketTimeoutException} if
* the timeout elapses before a connection is established. The default value
- * of {@code 0} disables connect timeouts; connect attempts may wait
- * indefinitely.
+ * of {@code 0} causes us to do a blocking connect. This does not mean we
+ * will never time out, but it probably means you'll get a TCP timeout
+ * after several minutes.
*
* <p><strong>Warning:</strong> if the hostname resolves to multiple IP
* addresses, this client will try each in <a
@@ -961,7 +962,7 @@ public abstract class URLConnection {
* elapse before the connect attempt throws an exception. Host names that
* support both IPv6 and IPv4 always have at least 2 IP addresses.
*
- * @param timeoutMillis the connect timeout in milliseconds. Non-negative.
+ * @throws IllegalArgumentException if {@code timeoutMillis &lt; 0}.
*/
public void setConnectTimeout(int timeoutMillis) {
if (timeoutMillis < 0) {
@@ -971,8 +972,7 @@ public abstract class URLConnection {
}
/**
- * Returns the connect timeout in milliseconds, or {@code 0} if connect
- * attempts never timeout.
+ * Returns the connect timeout in milliseconds. (See {#setConnectTimeout}.)
*/
public int getConnectTimeout() {
return connectTimeout;
diff --git a/luni/src/main/java/java/nio/Buffer.java b/luni/src/main/java/java/nio/Buffer.java
index c3840a5..b90744b 100644
--- a/luni/src/main/java/java/nio/Buffer.java
+++ b/luni/src/main/java/java/nio/Buffer.java
@@ -271,7 +271,7 @@ public abstract class Buffer {
final void checkWritable() {
if (isReadOnly()) {
- throw new IllegalArgumentException("read-only buffer");
+ throw new IllegalArgumentException("Read-only buffer");
}
}
diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java
index ef725c1..6a3624a 100644
--- a/luni/src/main/java/java/nio/ByteBuffer.java
+++ b/luni/src/main/java/java/nio/ByteBuffer.java
@@ -47,7 +47,7 @@ public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer
*/
public static ByteBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteHeapByteBuffer(capacity);
}
@@ -63,7 +63,7 @@ public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer
*/
public static ByteBuffer allocateDirect(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteDirectByteBuffer(capacity);
}
diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java
index 03bac04..6429ae2 100644
--- a/luni/src/main/java/java/nio/CharBuffer.java
+++ b/luni/src/main/java/java/nio/CharBuffer.java
@@ -49,7 +49,7 @@ public abstract class CharBuffer extends Buffer implements
*/
public static CharBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteCharArrayBuffer(capacity);
}
@@ -500,7 +500,7 @@ public abstract class CharBuffer extends Buffer implements
*/
public CharBuffer put(CharBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
@@ -734,7 +734,7 @@ public abstract class CharBuffer extends Buffer implements
if (remaining == 0) {
return -1;
}
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("target == this");
}
if (remaining == 0) {
return limit > 0 && target.remaining() == 0 ? 0 : -1;
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index a0e064d..4d2fc5a 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -448,7 +448,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
*/
private void checkNotNull(ByteBuffer source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
}
diff --git a/luni/src/main/java/java/nio/DoubleBuffer.java b/luni/src/main/java/java/nio/DoubleBuffer.java
index c495592..8d90f89 100644
--- a/luni/src/main/java/java/nio/DoubleBuffer.java
+++ b/luni/src/main/java/java/nio/DoubleBuffer.java
@@ -47,7 +47,7 @@ public abstract class DoubleBuffer extends Buffer implements
*/
public static DoubleBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteDoubleArrayBuffer(capacity);
}
@@ -438,7 +438,7 @@ public abstract class DoubleBuffer extends Buffer implements
*/
public DoubleBuffer put(DoubleBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 76bad51..075243a 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -442,7 +442,7 @@ final class FileChannelImpl extends FileChannel {
public FileChannel truncate(long size) throws IOException {
checkOpen();
if (size < 0) {
- throw new IllegalArgumentException("size: " + size);
+ throw new IllegalArgumentException("size < 0: " + size);
}
checkWritable();
if (size < size()) {
@@ -457,7 +457,7 @@ final class FileChannelImpl extends FileChannel {
public int write(ByteBuffer buffer, long position) throws IOException {
if (position < 0) {
- throw new IllegalArgumentException("position: " + position);
+ throw new IllegalArgumentException("position < 0: " + position);
}
return writeImpl(buffer, position);
}
diff --git a/luni/src/main/java/java/nio/FloatBuffer.java b/luni/src/main/java/java/nio/FloatBuffer.java
index ec361d6..814eb53 100644
--- a/luni/src/main/java/java/nio/FloatBuffer.java
+++ b/luni/src/main/java/java/nio/FloatBuffer.java
@@ -46,7 +46,7 @@ public abstract class FloatBuffer extends Buffer implements
*/
public static FloatBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteFloatArrayBuffer(capacity);
}
@@ -437,7 +437,7 @@ public abstract class FloatBuffer extends Buffer implements
*/
public FloatBuffer put(FloatBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/IntBuffer.java b/luni/src/main/java/java/nio/IntBuffer.java
index 9cc05ff..0ff758a 100644
--- a/luni/src/main/java/java/nio/IntBuffer.java
+++ b/luni/src/main/java/java/nio/IntBuffer.java
@@ -44,7 +44,7 @@ public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer>
*/
public static IntBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteIntArrayBuffer(capacity);
}
@@ -423,7 +423,7 @@ public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer>
*/
public IntBuffer put(IntBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/LongBuffer.java b/luni/src/main/java/java/nio/LongBuffer.java
index 27edd2e..1254ddb 100644
--- a/luni/src/main/java/java/nio/LongBuffer.java
+++ b/luni/src/main/java/java/nio/LongBuffer.java
@@ -46,7 +46,7 @@ public abstract class LongBuffer extends Buffer implements
*/
public static LongBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteLongArrayBuffer(capacity);
}
@@ -427,7 +427,7 @@ public abstract class LongBuffer extends Buffer implements
*/
public LongBuffer put(LongBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/MappedByteBuffer.java b/luni/src/main/java/java/nio/MappedByteBuffer.java
index 39c4986..0e8bf09 100644
--- a/luni/src/main/java/java/nio/MappedByteBuffer.java
+++ b/luni/src/main/java/java/nio/MappedByteBuffer.java
@@ -45,7 +45,7 @@ public abstract class MappedByteBuffer extends ByteBuffer {
MappedByteBuffer(ByteBuffer directBuffer) {
super(directBuffer.capacity, directBuffer.block);
if (!directBuffer.isDirect()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("directBuffer is not a direct buffer: " + directBuffer);
}
this.wrapped = (DirectByteBuffer) directBuffer;
this.mapMode = null;
diff --git a/luni/src/main/java/java/nio/NIOAccess.java b/luni/src/main/java/java/nio/NIOAccess.java
index 361a37f..36c41cf 100644
--- a/luni/src/main/java/java/nio/NIOAccess.java
+++ b/luni/src/main/java/java/nio/NIOAccess.java
@@ -28,7 +28,7 @@ final class NIOAccess {
* different than what the Harmony implementation calls a "base
* address."
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the native pointer to the Buffer's data at its current
* position, or 0 if there is none
*/
@@ -44,7 +44,7 @@ final class NIOAccess {
* Returns the underlying Java array containing the data of the
* given Buffer, or null if the Buffer is not backed by a Java array.
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the Java array containing the Buffer's data, or null if
* there is none
*/
@@ -55,13 +55,14 @@ final class NIOAccess {
/**
* Returns the offset in bytes from the start of the underlying
* Java array object containing the data of the given Buffer to
- * the actual start of the data. This method is only meaningful if
- * getBaseArray() returns non-null.
+ * the actual start of the data. The start of the data takes into
+ * account the Buffer's current position. This method is only
+ * meaningful if getBaseArray() returns non-null.
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the data offset in bytes to the start of this Buffer's data
*/
static int getBaseArrayOffset(Buffer b) {
- return b.hasArray() ? (b.arrayOffset() << b._elementSizeShift) : 0;
+ return b.hasArray() ? ((b.arrayOffset() + b.position) << b._elementSizeShift) : 0;
}
}
diff --git a/luni/src/main/java/java/nio/PipeImpl.java b/luni/src/main/java/java/nio/PipeImpl.java
index 50028f4..d4dde3b 100644
--- a/luni/src/main/java/java/nio/PipeImpl.java
+++ b/luni/src/main/java/java/nio/PipeImpl.java
@@ -19,8 +19,8 @@ package java.nio;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
+import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
@@ -34,13 +34,16 @@ final class PipeImpl extends Pipe {
private final PipeSinkChannel sink;
private final PipeSourceChannel source;
- public PipeImpl() throws IOException {
+ public PipeImpl(SelectorProvider selectorProvider) throws IOException {
try {
- FileDescriptor[] fds = Libcore.os.pipe();
- // Which fd is used for which channel is important. Unix pipes are only guaranteed to be
- // unidirectional, and indeed are only unidirectional on Linux.
- this.sink = new PipeSinkChannel(fds[1]);
- this.source = new PipeSourceChannel(fds[0]);
+ FileDescriptor fd1 = new FileDescriptor();
+ FileDescriptor fd2 = new FileDescriptor();
+ Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2);
+
+ // It doesn't matter which file descriptor we use for which end;
+ // they're guaranteed to be indistinguishable.
+ this.sink = new PipeSinkChannel(selectorProvider, fd1);
+ this.source = new PipeSourceChannel(selectorProvider, fd2);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
@@ -54,27 +57,14 @@ final class PipeImpl extends Pipe {
return source;
}
- /**
- * FileChannelImpl doesn't close its fd itself; it calls close on the object it's given.
- */
- private static class FdCloser implements Closeable {
- private final FileDescriptor fd;
- private FdCloser(FileDescriptor fd) {
- this.fd = fd;
- }
- public void close() throws IOException {
- IoUtils.close(fd);
- }
- }
-
private class PipeSourceChannel extends Pipe.SourceChannel implements FileDescriptorChannel {
private final FileDescriptor fd;
- private final FileChannel channel;
+ private final SocketChannel channel;
- private PipeSourceChannel(FileDescriptor fd) throws IOException {
- super(SelectorProvider.provider());
+ private PipeSourceChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException {
+ super(selectorProvider);
this.fd = fd;
- this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_RDONLY);
+ this.channel = new SocketChannelImpl(selectorProvider, fd);
}
@Override protected void implCloseSelectableChannel() throws IOException {
@@ -104,12 +94,12 @@ final class PipeImpl extends Pipe {
private class PipeSinkChannel extends Pipe.SinkChannel implements FileDescriptorChannel {
private final FileDescriptor fd;
- private final FileChannel channel;
+ private final SocketChannel channel;
- private PipeSinkChannel(FileDescriptor fd) throws IOException {
- super(SelectorProvider.provider());
+ private PipeSinkChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException {
+ super(selectorProvider);
this.fd = fd;
- this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_WRONLY);
+ this.channel = new SocketChannelImpl(selectorProvider, fd);
}
@Override protected void implCloseSelectableChannel() throws IOException {
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 05a6497..02fdf54 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -150,7 +150,7 @@ final class SelectorImpl extends AbstractSelector {
@Override public int select(long timeout) throws IOException {
if (timeout < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("timeout < 0: " + timeout);
}
// Our timeout is interpreted differently to Unix's --- 0 means block. See selectNow.
return selectInternal((timeout == 0) ? -1 : timeout);
diff --git a/luni/src/main/java/java/nio/SelectorProviderImpl.java b/luni/src/main/java/java/nio/SelectorProviderImpl.java
index 03003fe..b7c34e8 100644
--- a/luni/src/main/java/java/nio/SelectorProviderImpl.java
+++ b/luni/src/main/java/java/nio/SelectorProviderImpl.java
@@ -34,7 +34,7 @@ public final class SelectorProviderImpl extends SelectorProvider {
}
public Pipe openPipe() throws IOException {
- return new PipeImpl();
+ return new PipeImpl(this);
}
public AbstractSelector openSelector() throws IOException {
diff --git a/luni/src/main/java/java/nio/ShortBuffer.java b/luni/src/main/java/java/nio/ShortBuffer.java
index 052cf6b..d12a49e 100644
--- a/luni/src/main/java/java/nio/ShortBuffer.java
+++ b/luni/src/main/java/java/nio/ShortBuffer.java
@@ -46,7 +46,7 @@ public abstract class ShortBuffer extends Buffer implements
*/
public static ShortBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteShortArrayBuffer(capacity);
}
@@ -426,7 +426,7 @@ public abstract class ShortBuffer extends Buffer implements
*/
public ShortBuffer put(ShortBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 9b26812..233daf0 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -103,6 +103,15 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
}
/*
+ * Constructor for use by Pipe.SinkChannel and Pipe.SourceChannel.
+ */
+ public SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor existingFd) throws IOException {
+ super(selectorProvider);
+ status = SOCKET_STATUS_CONNECTED;
+ fd = existingFd;
+ }
+
+ /*
* Getting the internal Socket If we have not the socket, we create a new
* one.
*/
@@ -318,7 +327,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
@Override
public int write(ByteBuffer src) throws IOException {
if (src == null) {
- throw new NullPointerException();
+ throw new NullPointerException("src == null");
}
checkOpenConnected();
if (!src.hasRemaining()) {
@@ -412,7 +421,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
*/
static InetSocketAddress validateAddress(SocketAddress socketAddress) {
if (socketAddress == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("socketAddress == null");
}
if (!(socketAddress instanceof InetSocketAddress)) {
throw new UnsupportedAddressTypeException();
diff --git a/luni/src/main/java/java/nio/channels/Channels.java b/luni/src/main/java/java/nio/channels/Channels.java
index 3af1465..b59eeac 100644
--- a/luni/src/main/java/java/nio/channels/Channels.java
+++ b/luni/src/main/java/java/nio/channels/Channels.java
@@ -150,7 +150,7 @@ public final class Channels {
public static Reader newReader(ReadableByteChannel channel,
String charsetName) {
if (charsetName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("charsetName == null");
}
return newReader(channel, Charset.forName(charsetName).newDecoder(), -1);
}
@@ -193,7 +193,7 @@ public final class Channels {
public static Writer newWriter(WritableByteChannel channel,
String charsetName) {
if (charsetName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("charsetName == null");
}
return newWriter(channel, Charset.forName(charsetName).newEncoder(), -1);
}
@@ -207,7 +207,7 @@ public final class Channels {
ChannelInputStream(ReadableByteChannel channel) {
if (channel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("channel == null");
}
this.channel = channel;
}
@@ -247,7 +247,7 @@ public final class Channels {
ChannelOutputStream(WritableByteChannel channel) {
if (channel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("channel == null");
}
this.channel = channel;
}
@@ -289,7 +289,7 @@ public final class Channels {
InputStreamChannel(InputStream inputStream) {
if (inputStream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("inputStream == null");
}
this.inputStream = inputStream;
}
@@ -328,7 +328,7 @@ public final class Channels {
OutputStreamChannel(OutputStream outputStream) {
if (outputStream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("outputStream == null");
}
this.outputStream = outputStream;
}
diff --git a/luni/src/main/java/java/nio/channels/FileLock.java b/luni/src/main/java/java/nio/channels/FileLock.java
index 0916be0..4cdcc27 100644
--- a/luni/src/main/java/java/nio/channels/FileLock.java
+++ b/luni/src/main/java/java/nio/channels/FileLock.java
@@ -98,7 +98,7 @@ public abstract class FileLock {
*/
protected FileLock(FileChannel channel, long position, long size, boolean shared) {
if (position < 0 || size < 0 || position + size < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("position=" + position + " size=" + size);
}
this.channel = channel;
this.position = position;
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
index f67dbbc..1559db4 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
@@ -590,7 +590,7 @@ public abstract class CharsetDecoder {
*/
public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
if (newAction == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("newAction == null");
}
malformedInputAction = newAction;
implOnMalformedInput(newAction);
@@ -612,7 +612,7 @@ public abstract class CharsetDecoder {
*/
public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) {
if (newAction == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("newAction == null");
}
unmappableCharacterAction = newAction;
implOnUnmappableCharacter(newAction);
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
index 3ad46d7..8b32efa 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
@@ -16,7 +16,7 @@ package java.nio.charset;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
-import libcore.icu.ErrorCode;
+import libcore.icu.ICU;
import libcore.icu.NativeConverter;
import libcore.util.EmptyArray;
@@ -46,7 +46,6 @@ final class CharsetDecoderICU extends CharsetDecoder {
// is inherently thread-unsafe so we don't have to worry about synchronization.
private int inEnd;
private int outEnd;
- private int ec;
public static CharsetDecoderICU newInstance(Charset cs, String icuCanonicalName) {
// This complexity is necessary to ensure that even if the constructor, superclass
@@ -84,10 +83,7 @@ final class CharsetDecoderICU extends CharsetDecoder {
}
private void updateCallback() {
- ec = NativeConverter.setCallbackDecode(converterHandle, this);
- if (ErrorCode.isFailure(ec)) {
- throw ErrorCode.throwException(ec);
- }
+ NativeConverter.setCallbackDecode(converterHandle, this);
}
@Override protected void implReset() {
@@ -99,7 +95,6 @@ final class CharsetDecoderICU extends CharsetDecoder {
input = null;
allocatedInput = null;
allocatedOutput = null;
- ec = 0;
inEnd = 0;
outEnd = 0;
}
@@ -114,16 +109,14 @@ final class CharsetDecoderICU extends CharsetDecoder {
data[OUTPUT_OFFSET] = getArray(out);
data[INVALID_BYTES] = 0; // Make sure we don't see earlier errors.
- ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) {
+ } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) {
if (data[INPUT_OFFSET] > 0) {
return CoderResult.malformedForLength(data[INPUT_OFFSET]);
}
- } else {
- throw ErrorCode.throwException(ec);
}
}
return CoderResult.UNDERFLOW;
@@ -142,13 +135,17 @@ final class CharsetDecoderICU extends CharsetDecoder {
data[OUTPUT_OFFSET]= getArray(out);
try {
- ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false);
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
- return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) {
- return CoderResult.unmappableForLength(data[INVALID_BYTES]);
- } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) {
- return CoderResult.malformedForLength(data[INVALID_BYTES]);
+ int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
+ return CoderResult.OVERFLOW;
+ } else if (error == ICU.U_INVALID_CHAR_FOUND) {
+ return CoderResult.unmappableForLength(data[INVALID_BYTES]);
+ } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) {
+ return CoderResult.malformedForLength(data[INVALID_BYTES]);
+ } else {
+ throw new AssertionError(error);
+ }
}
// Decoding succeeded: give us more data.
return CoderResult.UNDERFLOW;
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 28b2cb8..8f02f96 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -706,11 +706,11 @@ public abstract class CharsetEncoder {
throw new IllegalArgumentException("replacement.length == 0");
}
if (replacement.length > maxBytesPerChar()) {
- throw new IllegalArgumentException("replacement length > maxBytesPerChar: " +
+ throw new IllegalArgumentException("replacement.length > maxBytesPerChar: " +
replacement.length + " > " + maxBytesPerChar());
}
if (!isLegalReplacement(replacement)) {
- throw new IllegalArgumentException("bad replacement: " + Arrays.toString(replacement));
+ throw new IllegalArgumentException("Bad replacement: " + Arrays.toString(replacement));
}
// It seems like a bug, but the RI doesn't clone, and we have tests that check we don't.
this.replacementBytes = replacement;
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
index cf071ca..76807b0 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
@@ -18,7 +18,7 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
-import libcore.icu.ErrorCode;
+import libcore.icu.ICU;
import libcore.icu.NativeConverter;
import libcore.util.EmptyArray;
@@ -61,7 +61,6 @@ final class CharsetEncoderICU extends CharsetEncoder {
// is inherently thread-unsafe so we don't have to worry about synchronization.
private int inEnd;
private int outEnd;
- private int ec;
public static CharsetEncoderICU newInstance(Charset cs, String icuCanonicalName) {
// This complexity is necessary to ensure that even if the constructor, superclass
@@ -112,10 +111,7 @@ final class CharsetEncoderICU extends CharsetEncoder {
}
private void updateCallback() {
- ec = NativeConverter.setCallbackEncode(converterHandle, this);
- if (ErrorCode.isFailure(ec)) {
- throw ErrorCode.throwException(ec);
- }
+ NativeConverter.setCallbackEncode(converterHandle, this);
}
@Override protected void implReset() {
@@ -127,7 +123,6 @@ final class CharsetEncoderICU extends CharsetEncoder {
input = null;
allocatedInput = null;
allocatedOutput = null;
- ec = 0;
inEnd = 0;
outEnd = 0;
}
@@ -142,16 +137,14 @@ final class CharsetEncoderICU extends CharsetEncoder {
data[OUTPUT_OFFSET] = getArray(out);
data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors.
- ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) {
+ } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) {
if (data[INPUT_OFFSET] > 0) {
return CoderResult.malformedForLength(data[INPUT_OFFSET]);
}
- } else {
- throw ErrorCode.throwException(ec);
}
}
return CoderResult.UNDERFLOW;
@@ -171,16 +164,16 @@ final class CharsetEncoderICU extends CharsetEncoder {
data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors.
try {
- ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) {
+ } else if (error == ICU.U_INVALID_CHAR_FOUND) {
return CoderResult.unmappableForLength(data[INVALID_CHARS]);
- } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) {
+ } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) {
return CoderResult.malformedForLength(data[INVALID_CHARS]);
} else {
- throw new AssertionError("unexpected failure: " + ec);
+ throw new AssertionError(error);
}
}
// Decoding succeeded: give us more data.
diff --git a/luni/src/main/java/java/nio/charset/Charsets.java b/luni/src/main/java/java/nio/charset/Charsets.java
index 1c2425a..826b12a 100644
--- a/luni/src/main/java/java/nio/charset/Charsets.java
+++ b/luni/src/main/java/java/nio/charset/Charsets.java
@@ -25,7 +25,7 @@ package java.nio.charset;
*
* @hide internal use only
*/
-public class Charsets {
+public final class Charsets {
/**
* A cheap and type-safe constant for the ISO-8859-1 Charset.
*/
diff --git a/luni/src/main/java/java/nio/charset/CoderResult.java b/luni/src/main/java/java/nio/charset/CoderResult.java
index 221cb32..3cc2673 100644
--- a/luni/src/main/java/java/nio/charset/CoderResult.java
+++ b/luni/src/main/java/java/nio/charset/CoderResult.java
@@ -121,7 +121,7 @@ public class CoderResult {
return r;
}
}
- throw new IllegalArgumentException("Length must be greater than 0; was " + length);
+ throw new IllegalArgumentException("length <= 0: " + length);
}
/**
@@ -149,7 +149,7 @@ public class CoderResult {
return r;
}
}
- throw new IllegalArgumentException("Length must be greater than 0; was " + length);
+ throw new IllegalArgumentException("length <= 0: " + length);
}
/**
diff --git a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
index daefa3c..3edc167 100644
--- a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
+++ b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
@@ -88,7 +88,7 @@ public class AlgorithmParameterGenerator {
public static AlgorithmParameterGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) sap.spi,
@@ -149,7 +149,7 @@ public class AlgorithmParameterGenerator {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, provider,
diff --git a/luni/src/main/java/java/security/AlgorithmParameters.java b/luni/src/main/java/java/security/AlgorithmParameters.java
index 8bbbe1b..073460e 100644
--- a/luni/src/main/java/java/security/AlgorithmParameters.java
+++ b/luni/src/main/java/java/security/AlgorithmParameters.java
@@ -92,7 +92,7 @@ public class AlgorithmParameters {
public static AlgorithmParameters getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new AlgorithmParameters((AlgorithmParametersSpi) sap.spi, sap.provider, algorithm);
@@ -120,7 +120,7 @@ public class AlgorithmParameters {
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider p = Security.getProvider(provider);
if (p == null) {
@@ -148,10 +148,10 @@ public class AlgorithmParameters {
public static AlgorithmParameters getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new AlgorithmParameters((AlgorithmParametersSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/KeyFactory.java b/luni/src/main/java/java/security/KeyFactory.java
index 554885a..8d39003 100644
--- a/luni/src/main/java/java/security/KeyFactory.java
+++ b/luni/src/main/java/java/security/KeyFactory.java
@@ -76,7 +76,7 @@ public class KeyFactory {
public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyFactory((KeyFactorySpi) sap.spi, sap.provider, algorithm);
@@ -130,7 +130,7 @@ public class KeyFactory {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyFactory((KeyFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/KeyPairGenerator.java b/luni/src/main/java/java/security/KeyPairGenerator.java
index d5851fa..5c17d79 100644
--- a/luni/src/main/java/java/security/KeyPairGenerator.java
+++ b/luni/src/main/java/java/security/KeyPairGenerator.java
@@ -80,7 +80,7 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi {
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -143,7 +143,7 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
if (spi instanceof KeyPairGenerator) {
diff --git a/luni/src/main/java/java/security/KeyStore.java b/luni/src/main/java/java/security/KeyStore.java
index c233a5b..3d856f7 100644
--- a/luni/src/main/java/java/security/KeyStore.java
+++ b/luni/src/main/java/java/security/KeyStore.java
@@ -110,7 +110,7 @@ public class KeyStore {
*/
public static KeyStore getInstance(String type) throws KeyStoreException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, null);
@@ -182,7 +182,7 @@ public class KeyStore {
throw new IllegalArgumentException();
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
// return KeyStore instance
try {
diff --git a/luni/src/main/java/java/security/KeyStoreSpi.java b/luni/src/main/java/java/security/KeyStoreSpi.java
index 01565f5..5ae9a72 100644
--- a/luni/src/main/java/java/security/KeyStoreSpi.java
+++ b/luni/src/main/java/java/security/KeyStoreSpi.java
@@ -414,14 +414,14 @@ public abstract class KeyStoreSpi {
}
char[] passW = null;
- if (protParam instanceof KeyStore.PasswordProtection) {
- try {
- passW = ((KeyStore.PasswordProtection) protParam).getPassword();
- } catch (IllegalStateException ee) {
- throw new KeyStoreException("Password was destroyed", ee);
- }
- } else {
- if (protParam instanceof KeyStore.CallbackHandlerProtection) {
+ if (protParam != null) {
+ if (protParam instanceof KeyStore.PasswordProtection) {
+ try {
+ passW = ((KeyStore.PasswordProtection) protParam).getPassword();
+ } catch (IllegalStateException ee) {
+ throw new KeyStoreException("Password was destroyed", ee);
+ }
+ } else if (protParam instanceof KeyStore.CallbackHandlerProtection) {
try {
passW = getPasswordFromCallBack(protParam);
} catch (Exception e) {
diff --git a/luni/src/main/java/java/security/MessageDigest.java b/luni/src/main/java/java/security/MessageDigest.java
index 3154028..6f1bf21 100644
--- a/luni/src/main/java/java/security/MessageDigest.java
+++ b/luni/src/main/java/java/security/MessageDigest.java
@@ -86,7 +86,7 @@ public abstract class MessageDigest extends MessageDigestSpi {
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -152,7 +152,7 @@ public abstract class MessageDigest extends MessageDigestSpi {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
if (spi instanceof MessageDigest) {
@@ -217,7 +217,7 @@ public abstract class MessageDigest extends MessageDigestSpi {
*/
public void update(byte[] input) {
if (input == null) {
- throw new NullPointerException();
+ throw new NullPointerException("input == null");
}
engineUpdate(input, 0, input.length);
}
diff --git a/luni/src/main/java/java/security/Provider.java b/luni/src/main/java/java/security/Provider.java
index 2de3751..899625a 100644
--- a/luni/src/main/java/java/security/Provider.java
+++ b/luni/src/main/java/java/security/Provider.java
@@ -27,12 +27,13 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import org.apache.harmony.luni.util.TwoKeyHashMap;
import org.apache.harmony.security.fortress.Services;
/**
@@ -57,18 +58,18 @@ public abstract class Provider extends Properties {
// Contains "Service.Algorithm" and Provider.Service classes added using
// putService()
- private transient TwoKeyHashMap<String, String, Service> serviceTable;
+ private transient LinkedHashMap<String, Service> serviceTable;
// Contains "Service.Alias" and Provider.Service classes added using
// putService()
- private transient TwoKeyHashMap<String, String, Service> aliasTable;
+ private transient LinkedHashMap<String, Service> aliasTable;
// Contains "Service.Algorithm" and Provider.Service classes added using
// put()
- private transient TwoKeyHashMap<String, String, Service> propertyServiceTable;
+ private transient LinkedHashMap<String, Service> propertyServiceTable;
// Contains "Service.Alias" and Provider.Service classes added using put()
- private transient TwoKeyHashMap<String, String, Service> propertyAliasTable;
+ private transient LinkedHashMap<String, Service> propertyAliasTable;
// The properties changed via put()
private transient Properties changedProperties;
@@ -406,30 +407,32 @@ public abstract class Provider extends Properties {
*/
public synchronized Provider.Service getService(String type,
String algorithm) {
- if (type == null || algorithm == null) {
- throw new NullPointerException();
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ } else if (algorithm == null) {
+ throw new NullPointerException("algorithm == null");
}
if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) {
return returnedService;
}
- String alg = algorithm.toUpperCase(Locale.US);
+ String key = key(type, algorithm);
Object o = null;
if (serviceTable != null) {
- o = serviceTable.get(type, alg);
+ o = serviceTable.get(key);
}
if (o == null && aliasTable != null) {
- o = aliasTable.get(type, alg);
+ o = aliasTable.get(key);
}
if (o == null) {
updatePropertyServiceTable();
}
if (o == null && propertyServiceTable != null) {
- o = propertyServiceTable.get(type, alg);
+ o = propertyServiceTable.get(key);
}
if (o == null && propertyAliasTable != null) {
- o = propertyAliasTable.get(type, alg);
+ o = propertyAliasTable.get(key);
}
if (o != null) {
@@ -454,9 +457,9 @@ public abstract class Provider extends Properties {
return lastServicesSet;
}
if (serviceTable != null) {
- lastServicesSet = new HashSet<Service>(serviceTable.values());
+ lastServicesSet = new LinkedHashSet<Service>(serviceTable.values());
} else {
- lastServicesSet = new HashSet<Service>();
+ lastServicesSet = new LinkedHashSet<Service>();
}
if (propertyServiceTable != null) {
lastServicesSet.addAll(propertyServiceTable.values());
@@ -474,22 +477,22 @@ public abstract class Provider extends Properties {
*/
protected synchronized void putService(Provider.Service s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
if ("Provider".equals(s.getType())) { // Provider service type cannot be added
return;
}
servicesChanged();
if (serviceTable == null) {
- serviceTable = new TwoKeyHashMap<String, String, Service>(128);
+ serviceTable = new LinkedHashMap<String, Service>(128);
}
- serviceTable.put(s.type, s.algorithm.toUpperCase(Locale.US), s);
+ serviceTable.put(key(s.type, s.algorithm), s);
if (s.aliases != null) {
if (aliasTable == null) {
- aliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ aliasTable = new LinkedHashMap<String, Service>(256);
}
for (String alias : s.getAliases()) {
- aliasTable.put(s.type, alias.toUpperCase(Locale.US), s);
+ aliasTable.put(key(s.type, alias), s);
}
}
serviceInfoToProperties(s);
@@ -506,15 +509,15 @@ public abstract class Provider extends Properties {
*/
protected synchronized void removeService(Provider.Service s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
servicesChanged();
if (serviceTable != null) {
- serviceTable.remove(s.type, s.algorithm.toUpperCase(Locale.US));
+ serviceTable.remove(key(s.type, s.algorithm));
}
if (aliasTable != null && s.aliases != null) {
for (String alias: s.getAliases()) {
- aliasTable.remove(s.type, alias.toUpperCase(Locale.US));
+ aliasTable.remove(key(s.type, alias));
}
}
serviceInfoFromProperties(s);
@@ -584,7 +587,7 @@ public abstract class Provider extends Properties {
serviceName = service_alias.substring(0, i);
aliasName = service_alias.substring(i + 1);
if (propertyAliasTable != null) {
- propertyAliasTable.remove(serviceName, aliasName.toUpperCase(Locale.US));
+ propertyAliasTable.remove(key(serviceName, aliasName));
}
if (propertyServiceTable != null) {
for (Iterator<Service> it = propertyServiceTable.values().iterator(); it
@@ -608,12 +611,11 @@ public abstract class Provider extends Properties {
serviceName = k.substring(0, j);
algorithm = k.substring(j + 1);
if (propertyServiceTable != null) {
- Provider.Service ser = propertyServiceTable.remove(serviceName,
- algorithm.toUpperCase(Locale.US));
+ Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm));
if (ser != null && propertyAliasTable != null
&& ser.aliases != null) {
for (String alias : ser.aliases) {
- propertyAliasTable.remove(serviceName, alias.toUpperCase(Locale.US));
+ propertyAliasTable.remove(key(serviceName, alias));
}
}
}
@@ -624,7 +626,7 @@ public abstract class Provider extends Properties {
serviceName = k.substring(0, j);
algorithm = k.substring(j + 1, i);
if (propertyServiceTable != null) {
- Object o = propertyServiceTable.get(serviceName, algorithm.toUpperCase(Locale.US));
+ Object o = propertyServiceTable.get(key(serviceName, algorithm));
if (o != null) {
s = (Provider.Service) o;
s.attributes.remove(attribute);
@@ -667,20 +669,20 @@ public abstract class Provider extends Properties {
serviceName = service_alias.substring(0, i);
aliasName = service_alias.substring(i + 1);
algorithm = value;
- String algUp = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
} else {
- o = propertyServiceTable.get(serviceName, algUp);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
s.addAlias(aliasName);
if (propertyAliasTable == null) {
- propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ propertyAliasTable = new LinkedHashMap<String, Service>(256);
}
- propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s);
+ propertyAliasTable.put(key(serviceName, aliasName), s);
} else {
String className = (String) changedProperties
.get(serviceName + "." + algorithm);
@@ -689,11 +691,11 @@ public abstract class Provider extends Properties {
l.add(aliasName);
s = new Provider.Service(this, serviceName, algorithm,
className, l, new HashMap<String, String>());
- propertyServiceTable.put(serviceName, algUp, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
if (propertyAliasTable == null) {
- propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ propertyAliasTable = new LinkedHashMap<String, Service>(256);
}
- propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s);
+ propertyAliasTable.put(key(serviceName, aliasName), s);
}
}
continue;
@@ -706,10 +708,10 @@ public abstract class Provider extends Properties {
if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
serviceName = key.substring(0, j);
algorithm = key.substring(j + 1);
- String alg = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable != null) {
- o = propertyServiceTable.get(serviceName, alg);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
@@ -719,21 +721,20 @@ public abstract class Provider extends Properties {
value, Collections.<String>emptyList(),
Collections.<String,String>emptyMap());
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
}
- propertyServiceTable.put(serviceName, alg, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
}
} else {
- // <crypto_service>.<algorithm_or_type>
- // <attribute_name>=<attrValue>
+ // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue>
serviceName = key.substring(0, j);
algorithm = key.substring(j + 1, i);
String attribute = key.substring(i + 1);
- String alg = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable != null) {
- o = propertyServiceTable.get(serviceName, alg);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
@@ -747,9 +748,9 @@ public abstract class Provider extends Properties {
s = new Provider.Service(this, serviceName, algorithm,
className, new ArrayList<String>(), m);
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
}
- propertyServiceTable.put(serviceName, alg, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
}
}
}
@@ -794,6 +795,10 @@ public abstract class Provider extends Properties {
return null;
}
+ private static String key(String type, String algorithm) {
+ return type + '.' + algorithm.toUpperCase(Locale.US);
+ }
+
/**
* {@code Service} represents a service in the Java Security infrastructure.
* Each service describes its type, the algorithm it implements, to which
@@ -849,9 +854,14 @@ public abstract class Provider extends Properties {
*/
public Service(Provider provider, String type, String algorithm,
String className, List<String> aliases, Map<String, String> attributes) {
- if (provider == null || type == null || algorithm == null
- || className == null) {
- throw new NullPointerException();
+ if (provider == null) {
+ throw new NullPointerException("provider == null");
+ } else if (type == null) {
+ throw new NullPointerException("type == null");
+ } else if (algorithm == null) {
+ throw new NullPointerException("algorithm == null");
+ } else if (className == null) {
+ throw new NullPointerException("className == null");
}
this.provider = provider;
this.type = type;
@@ -940,7 +950,7 @@ public abstract class Provider extends Properties {
*/
public final String getAttribute(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (attributes == null) {
return null;
diff --git a/luni/src/main/java/java/security/SecureRandom.java b/luni/src/main/java/java/security/SecureRandom.java
index 68a2917..6ed631c 100644
--- a/luni/src/main/java/java/security/SecureRandom.java
+++ b/luni/src/main/java/java/security/SecureRandom.java
@@ -88,7 +88,6 @@ public class SecureRandom extends Random {
*/
public SecureRandom() {
super(0);
- Services.refresh();
Provider.Service service = Services.getSecureRandomService();
if (service == null) {
this.provider = null;
@@ -154,7 +153,7 @@ public class SecureRandom extends Random {
*/
public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new SecureRandom((SecureRandomSpi) sap.spi, sap.provider,
@@ -213,7 +212,7 @@ public class SecureRandom extends Random {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new SecureRandom((SecureRandomSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index a4cc6e1..b5bd02a 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -227,7 +227,7 @@ public final class Security {
*/
public static Provider[] getProviders(String filter) {
if (filter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("filter == null");
}
if (filter.length() == 0) {
throw new InvalidParameterException();
@@ -271,7 +271,7 @@ public final class Security {
*/
public static synchronized Provider[] getProviders(Map<String,String> filter) {
if (filter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("filter == null");
}
if (filter.isEmpty()) {
return null;
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index d9e1e41..be89654 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -99,7 +99,7 @@ public abstract class Signature extends SignatureSpi {
public static Signature getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -134,7 +134,7 @@ public abstract class Signature extends SignatureSpi {
public static Signature getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException();
@@ -165,7 +165,7 @@ public abstract class Signature extends SignatureSpi {
public static Signature getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/security/Signer.java b/luni/src/main/java/java/security/Signer.java
index 1e4412a..b892090 100644
--- a/luni/src/main/java/java/security/Signer.java
+++ b/luni/src/main/java/java/security/Signer.java
@@ -83,7 +83,7 @@ public abstract class Signer extends Identity {
*/
public final void setKeyPair(KeyPair pair) throws InvalidParameterException, KeyException {
if (pair == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pair == null");
}
if (pair.getPrivate() == null || pair.getPublic() == null) {
diff --git a/luni/src/main/java/java/security/cert/CertPathBuilder.java b/luni/src/main/java/java/security/cert/CertPathBuilder.java
index aa65fe7..42029e5 100644
--- a/luni/src/main/java/java/security/cert/CertPathBuilder.java
+++ b/luni/src/main/java/java/security/cert/CertPathBuilder.java
@@ -102,7 +102,7 @@ public class CertPathBuilder {
public static CertPathBuilder getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new CertPathBuilder((CertPathBuilderSpi) sap.spi, sap.provider, algorithm);
@@ -128,7 +128,7 @@ public class CertPathBuilder {
public static CertPathBuilder getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -156,10 +156,10 @@ public class CertPathBuilder {
public static CertPathBuilder getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/cert/CertPathValidator.java b/luni/src/main/java/java/security/cert/CertPathValidator.java
index 69b9f99..ddf78bf 100644
--- a/luni/src/main/java/java/security/cert/CertPathValidator.java
+++ b/luni/src/main/java/java/security/cert/CertPathValidator.java
@@ -101,7 +101,7 @@ public class CertPathValidator {
public static CertPathValidator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new CertPathValidator((CertPathValidatorSpi) sap.spi, sap.provider, algorithm);
@@ -160,7 +160,7 @@ public class CertPathValidator {
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/cert/CertStore.java b/luni/src/main/java/java/security/cert/CertStore.java
index 6cdaea7..2e28828 100644
--- a/luni/src/main/java/java/security/cert/CertStore.java
+++ b/luni/src/main/java/java/security/cert/CertStore.java
@@ -97,7 +97,7 @@ public class CertStore {
public static CertStore getInstance(String type, CertStoreParameters params)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, params);
@@ -140,7 +140,7 @@ public class CertStore {
throws InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -172,10 +172,10 @@ public class CertStore {
CertStoreParameters params, Provider provider)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Object spi = ENGINE.getInstance(type, provider, params);
diff --git a/luni/src/main/java/java/security/cert/CertificateFactory.java b/luni/src/main/java/java/security/cert/CertificateFactory.java
index 1aac1a0..83d40d3 100644
--- a/luni/src/main/java/java/security/cert/CertificateFactory.java
+++ b/luni/src/main/java/java/security/cert/CertificateFactory.java
@@ -84,7 +84,7 @@ public class CertificateFactory {
public static final CertificateFactory getInstance(String type)
throws CertificateException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, null);
@@ -117,7 +117,7 @@ public class CertificateFactory {
String provider) throws CertificateException,
NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -147,10 +147,10 @@ public class CertificateFactory {
public static final CertificateFactory getInstance(String type,
Provider provider) throws CertificateException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Object spi = ENGINE.getInstance(type, provider, null);
diff --git a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
index de3c85d..9373b40 100644
--- a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
+++ b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
@@ -58,10 +58,10 @@ public class CollectionCertStoreParameters implements CertStoreParameters {
* if {@code collection is null}.
*/
public CollectionCertStoreParameters(Collection<?> collection) {
- this.collection = collection;
- if (this.collection == null) {
- throw new NullPointerException();
+ if (collection == null) {
+ throw new NullPointerException("collection == null");
}
+ this.collection = collection;
}
/**
diff --git a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
index 5b01f90..163c99a 100644
--- a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
+++ b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
@@ -43,11 +43,11 @@ public class LDAPCertStoreParameters implements CertStoreParameters {
* is {@code serverName} is {@code null}.
*/
public LDAPCertStoreParameters(String serverName, int port) {
+ if (serverName == null) {
+ throw new NullPointerException("serverName == null");
+ }
this.port = port;
this.serverName = serverName;
- if (this.serverName == null) {
- throw new NullPointerException();
- }
}
/**
@@ -71,11 +71,11 @@ public class LDAPCertStoreParameters implements CertStoreParameters {
* if {@code serverName} is {@code null}.
*/
public LDAPCertStoreParameters(String serverName) {
+ if (serverName == null) {
+ throw new NullPointerException("serverName == null");
+ }
this.port = DEFAULT_LDAP_PORT;
this.serverName = serverName;
- if (this.serverName == null) {
- throw new NullPointerException();
- }
}
/**
diff --git a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
index 589ce82..2359612 100644
--- a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
+++ b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
@@ -49,10 +49,10 @@ public class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult
public PKIXCertPathBuilderResult(CertPath certPath, TrustAnchor trustAnchor,
PolicyNode policyTree, PublicKey subjectPublicKey) {
super(trustAnchor, policyTree, subjectPublicKey);
- this.certPath = certPath;
- if (this.certPath == null) {
+ if (certPath == null) {
throw new NullPointerException("certPath == null");
}
+ this.certPath = certPath;
}
/**
diff --git a/luni/src/main/java/java/security/cert/X509CRL.java b/luni/src/main/java/java/security/cert/X509CRL.java
index 4badd59..4addb0e 100644
--- a/luni/src/main/java/java/security/cert/X509CRL.java
+++ b/luni/src/main/java/java/security/cert/X509CRL.java
@@ -218,7 +218,7 @@ public abstract class X509CRL extends CRL implements X509Extension {
*/
public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
if (certificate == null) {
- throw new NullPointerException();
+ throw new NullPointerException("certificate == null");
}
return getRevokedCertificate(certificate.getSerialNumber());
}
diff --git a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
index fe66b1e..c22038d 100644
--- a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
+++ b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
@@ -35,7 +35,7 @@ public class ECGenParameterSpec implements AlgorithmParameterSpec {
public ECGenParameterSpec(String name) {
this.name = name;
if (this.name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
}
diff --git a/luni/src/main/java/java/sql/DataTruncation.java b/luni/src/main/java/java/sql/DataTruncation.java
index b01100e..c1bf11c 100644
--- a/luni/src/main/java/java/sql/DataTruncation.java
+++ b/luni/src/main/java/java/sql/DataTruncation.java
@@ -49,7 +49,7 @@ public class DataTruncation extends SQLWarning implements Serializable {
/**
* Creates the {@code DataTruncation} object. The reason is set to {@code
- * "Data truncation"}, the {@code ErrorCode} is set to the {@code
+ * "Data truncation"}, the error code is set to the {@code
* SQLException} default value, and the other fields are set to the values
* supplied as arguments.
*
@@ -79,7 +79,7 @@ public class DataTruncation extends SQLWarning implements Serializable {
/**
* Creates a DataTruncation. The Reason is set to "Data truncation", the
- * ErrorCode is set to the SQLException default value and other fields are
+ * error code is set to the SQLException default value and other fields are
* set to the values supplied on this method.
*
* @param index
diff --git a/luni/src/main/java/java/sql/DriverManager.java b/luni/src/main/java/java/sql/DriverManager.java
index 4cee1fe..c547585 100644
--- a/luni/src/main/java/java/sql/DriverManager.java
+++ b/luni/src/main/java/java/sql/DriverManager.java
@@ -329,7 +329,7 @@ public class DriverManager {
*/
public static void registerDriver(Driver driver) throws SQLException {
if (driver == null) {
- throw new NullPointerException();
+ throw new NullPointerException("driver == null");
}
synchronized (theDrivers) {
theDrivers.add(driver);
diff --git a/luni/src/main/java/java/text/AttributedString.java b/luni/src/main/java/java/text/AttributedString.java
index 1335067..d679283 100644
--- a/luni/src/main/java/java/text/AttributedString.java
+++ b/luni/src/main/java/java/text/AttributedString.java
@@ -519,7 +519,7 @@ public class AttributedString {
*/
public AttributedString(String value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
text = value;
attributeMap = new HashMap<Attribute, List<Range>>(11);
@@ -542,7 +542,7 @@ public class AttributedString {
public AttributedString(String value,
Map<? extends AttributedCharacterIterator.Attribute, ?> attributes) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
if (value.length() == 0 && !attributes.isEmpty()) {
throw new IllegalArgumentException("Cannot add attributes to empty string");
@@ -575,7 +575,7 @@ public class AttributedString {
*/
public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value) {
if (attribute == null) {
- throw new NullPointerException();
+ throw new NullPointerException("attribute == null");
}
if (text.length() == 0) {
throw new IllegalArgumentException();
@@ -612,7 +612,7 @@ public class AttributedString {
public void addAttribute(AttributedCharacterIterator.Attribute attribute,
Object value, int start, int end) {
if (attribute == null) {
- throw new NullPointerException();
+ throw new NullPointerException("attribute == null");
}
if (start < 0 || end > text.length() || start >= end) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/text/Collator.java b/luni/src/main/java/java/text/Collator.java
index 0fa8c71..2ddb516 100644
--- a/luni/src/main/java/java/text/Collator.java
+++ b/luni/src/main/java/java/text/Collator.java
@@ -286,7 +286,7 @@ public abstract class Collator implements Comparator<Object>, Cloneable {
*/
public static Collator getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new RuleBasedCollator(new RuleBasedCollatorICU(locale));
}
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 3c4768d..e2a2345 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -142,7 +142,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
*/
public static final DateFormatSymbols getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new DateFormatSymbols(locale);
}
@@ -410,7 +410,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
*/
public void setLocalPatternChars(String data) {
if (data == null) {
- throw new NullPointerException();
+ throw new NullPointerException("data == null");
}
localPatternChars = data;
}
@@ -471,7 +471,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
*/
public void setZoneStrings(String[][] zoneStrings) {
if (zoneStrings == null) {
- throw new NullPointerException();
+ throw new NullPointerException("zoneStrings == null");
}
for (String[] row : zoneStrings) {
if (row.length < 5) {
diff --git a/luni/src/main/java/java/text/DecimalFormat.java b/luni/src/main/java/java/text/DecimalFormat.java
index c0d67b8..948bec1 100644
--- a/luni/src/main/java/java/text/DecimalFormat.java
+++ b/luni/src/main/java/java/text/DecimalFormat.java
@@ -642,7 +642,7 @@ public class DecimalFormat extends NumberFormat {
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
return ndf.formatToCharacterIterator(object);
}
@@ -1236,7 +1236,7 @@ public class DecimalFormat extends NumberFormat {
*/
public void setRoundingMode(RoundingMode roundingMode) {
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
this.roundingMode = roundingMode;
if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY.
diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java
index 9d2bcc1..708b291 100644
--- a/luni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -127,7 +127,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
*/
public static DecimalFormatSymbols getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new DecimalFormatSymbols(locale);
}
@@ -389,7 +389,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
*/
public void setCurrency(Currency currency) {
if (currency == null) {
- throw new NullPointerException();
+ throw new NullPointerException("currency == null");
}
if (currency == this.currency) {
return;
@@ -558,7 +558,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
*/
public void setExponentSeparator(String value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
this.exponentSeparator = value;
}
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index a98e4fd..2ab78db 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -506,7 +506,7 @@ public class MessageFormat extends Format {
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
StringBuffer buffer = new StringBuffer();
diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java
index 54a4849..c285e3d 100644
--- a/luni/src/main/java/java/text/NumberFormat.java
+++ b/luni/src/main/java/java/text/NumberFormat.java
@@ -566,7 +566,7 @@ public abstract class NumberFormat extends Format {
@Override
public final Object parseObject(String string, ParsePosition position) {
if (position == null) {
- throw new NullPointerException("position is null");
+ throw new NullPointerException("position == null");
}
try {
return parse(string, position);
diff --git a/luni/src/main/java/java/text/RuleBasedCollator.java b/luni/src/main/java/java/text/RuleBasedCollator.java
index 4fd8650..cda06db 100644
--- a/luni/src/main/java/java/text/RuleBasedCollator.java
+++ b/luni/src/main/java/java/text/RuleBasedCollator.java
@@ -284,7 +284,7 @@ public class RuleBasedCollator extends Collator {
*/
public RuleBasedCollator(String rules) throws ParseException {
if (rules == null) {
- throw new NullPointerException();
+ throw new NullPointerException("rules == null");
}
if (rules.isEmpty()) {
throw new ParseException("empty rules", 0);
@@ -314,7 +314,7 @@ public class RuleBasedCollator extends Collator {
*/
public CollationElementIterator getCollationElementIterator(CharacterIterator source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
return new CollationElementIterator(icuColl.getCollationElementIterator(source));
}
@@ -328,7 +328,7 @@ public class RuleBasedCollator extends Collator {
*/
public CollationElementIterator getCollationElementIterator(String source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
return new CollationElementIterator(icuColl.getCollationElementIterator(source));
}
@@ -385,8 +385,10 @@ public class RuleBasedCollator extends Collator {
*/
@Override
public int compare(String source, String target) {
- if (source == null || target == null) {
- throw new NullPointerException();
+ if (source == null) {
+ throw new NullPointerException("source == null");
+ } else if (target == null) {
+ throw new NullPointerException("target == null");
}
return icuColl.compare(source, target);
}
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 5cad44b..f682c0b 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -468,7 +468,7 @@ public class SimpleDateFormat extends DateFormat {
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
if (object instanceof Date) {
return formatToCharacterIteratorImpl((Date) object);
@@ -1067,24 +1067,42 @@ public class SimpleDateFormat extends DateFormat {
}
private Number parseNumber(int max, String string, ParsePosition position) {
- int digit, length = string.length(), result = 0;
+ int length = string.length();
int index = position.getIndex();
if (max > 0 && max < length - index) {
length = index + max;
}
- while (index < length
- && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) {
- index++;
+ while (index < length && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) {
+ ++index;
}
if (max == 0) {
position.setIndex(index);
- return numberFormat.parse(string, position);
+ Number n = numberFormat.parse(string, position);
+ // In RTL locales, NumberFormat might have parsed "2012-" in an ISO date as the
+ // negative number -2012.
+ // Ideally, we wouldn't have this broken API that exposes a NumberFormat and expects
+ // us to use it. The next best thing would be a way to ask the NumberFormat to parse
+ // positive numbers only, but icu4c supports negative (BCE) years. The best we can do
+ // is try to recognize when icu4c has done this, and undo it.
+ if (n != null && n.longValue() < 0) {
+ if (numberFormat instanceof DecimalFormat) {
+ DecimalFormat df = (DecimalFormat) numberFormat;
+ char lastChar = string.charAt(position.getIndex() - 1);
+ char minusSign = df.getDecimalFormatSymbols().getMinusSign();
+ if (lastChar == minusSign) {
+ n = Long.valueOf(-n.longValue()); // Make the value positive.
+ position.setIndex(position.getIndex() - 1); // Spit out the negative sign.
+ }
+ }
+ }
+ return n;
}
- while (index < length
- && (digit = Character.digit(string.charAt(index), 10)) != -1) {
- index++;
+ int result = 0;
+ int digit;
+ while (index < length && (digit = Character.digit(string.charAt(index), 10)) != -1) {
result = result * 10 + digit;
+ ++index;
}
if (index == position.getIndex()) {
position.setErrorIndex(index);
@@ -1171,14 +1189,18 @@ public class SimpleDateFormat extends DateFormat {
}
int raw = zone.getRawOffset();
if (j == TimeZones.LONG_NAME_DST || j == TimeZones.SHORT_NAME_DST) {
- /*
- * TODO, http://b/4723412
- * We can't use TimeZone#getDSTSavings here because that
- * will return 0 if the zone no longer uses DST. We
- * should change this to use TimeZone.getOffset(long),
- * which requires the complete date to be parsed first.
- */
- raw += 3600000;
+ // Not all time zones use a one-hour difference, so we need to query
+ // the TimeZone. (Australia/Lord_Howe is the usual example of this.)
+ int dstSavings = zone.getDSTSavings();
+ // One problem with TimeZone.getDSTSavings is that it will return 0 if the
+ // time zone has stopped using DST, even if we're parsing a date from
+ // the past. In that case, assume the default.
+ if (dstSavings == 0) {
+ // TODO: we should change this to use TimeZone.getOffset(long),
+ // but that requires the complete date to be parsed first.
+ dstSavings = 3600000;
+ }
+ raw += dstSavings;
}
calendar.setTimeZone(new SimpleTimeZone(raw, ""));
return offset + element[j].length();
diff --git a/luni/src/main/java/java/util/AbstractQueue.java b/luni/src/main/java/java/util/AbstractQueue.java
index d368ac9..47f81fd 100644
--- a/luni/src/main/java/java/util/AbstractQueue.java
+++ b/luni/src/main/java/java/util/AbstractQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
@@ -150,9 +150,9 @@ public abstract class AbstractQueue<E>
*/
public boolean addAll(Collection<? extends E> c) {
if (c == null)
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
if (c == this)
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("c == this");
boolean modified = false;
for (E e : c)
if (add(e))
diff --git a/luni/src/main/java/java/util/ArrayDeque.java b/luni/src/main/java/java/util/ArrayDeque.java
index fafcdb4..5ee3f81 100644
--- a/luni/src/main/java/java/util/ArrayDeque.java
+++ b/luni/src/main/java/java/util/ArrayDeque.java
@@ -1,6 +1,6 @@
/*
* Written by Josh Bloch of Google Inc. and released to the public domain,
- * as explained at http://creativecommons.org/licenses/publicdomain.
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/.
*/
package java.util;
@@ -9,8 +9,6 @@ package java.util;
// removed link to collections framework docs
// END android-note
-import java.io.*;
-
/**
* Resizable-array implementation of the {@link Deque} interface. Array
* deques have no capacity restrictions; they grow as necessary to support
@@ -53,7 +51,7 @@ import java.io.*;
* @param <E> the type of elements held in this collection
*/
public class ArrayDeque<E> extends AbstractCollection<E>
- implements Deque<E>, Cloneable, Serializable
+ implements Deque<E>, Cloneable, java.io.Serializable
{
/**
* The array in which the elements of the deque are stored.
@@ -65,7 +63,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* other. We also guarantee that all array cells not holding
* deque elements are always null.
*/
- private transient E[] elements;
+ private transient Object[] elements;
/**
* The index of the element at the head of the deque (which is the
@@ -109,7 +107,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
- elements = (E[]) new Object[initialCapacity];
+ elements = new Object[initialCapacity];
}
/**
@@ -127,7 +125,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
- elements = (E[])a;
+ elements = a;
head = 0;
tail = n;
}
@@ -155,7 +153,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* sufficient to hold 16 elements.
*/
public ArrayDeque() {
- elements = (E[]) new Object[16];
+ elements = new Object[16];
}
/**
@@ -195,7 +193,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
*/
public void addFirst(E e) {
if (e == null)
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
@@ -211,7 +209,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
*/
public void addLast(E e) {
if (e == null)
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
@@ -263,7 +261,8 @@ public class ArrayDeque<E> extends AbstractCollection<E>
public E pollFirst() {
int h = head;
- E result = elements[h]; // Element is null if deque empty
+ @SuppressWarnings("unchecked") E result = (E) elements[h];
+ // Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
@@ -273,7 +272,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
- E result = elements[t];
+ @SuppressWarnings("unchecked") E result = (E) elements[t];
if (result == null)
return null;
elements[t] = null;
@@ -285,28 +284,33 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* @throws NoSuchElementException {@inheritDoc}
*/
public E getFirst() {
- E x = elements[head];
- if (x == null)
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ if (result == null)
throw new NoSuchElementException();
- return x;
+ return result;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E getLast() {
- E x = elements[(tail - 1) & (elements.length - 1)];
- if (x == null)
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ if (result == null)
throw new NoSuchElementException();
- return x;
+ return result;
}
public E peekFirst() {
- return elements[head]; // elements[head] is null if deque empty
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ // elements[head] is null if deque empty
+ return result;
}
public E peekLast() {
- return elements[(tail - 1) & (elements.length - 1)];
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ return result;
}
/**
@@ -326,7 +330,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
return false;
int mask = elements.length - 1;
int i = head;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
@@ -354,7 +358,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
return false;
int mask = elements.length - 1;
int i = (tail - 1) & mask;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
@@ -499,7 +503,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
*/
private boolean delete(int i) {
checkInvariants();
- final E[] elements = this.elements;
+ final Object[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
@@ -597,7 +601,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
- E result = elements[cursor];
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
// This check doesn't catch all possible comodifications,
// but does catch the ones that corrupt traversal
if (tail != fence || result == null)
@@ -636,7 +640,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
if (cursor == fence)
throw new NoSuchElementException();
cursor = (cursor - 1) & (elements.length - 1);
- E result = elements[cursor];
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
if (head != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
@@ -667,7 +671,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
return false;
int mask = elements.length - 1;
int i = head;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x))
return true;
@@ -750,8 +754,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* The following code can be used to dump the deque into a newly
* allocated array of <tt>String</tt>:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -765,6 +768,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* this deque
* @throws NullPointerException if the specified array is null
*/
+ @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
@@ -785,6 +789,7 @@ public class ArrayDeque<E> extends AbstractCollection<E>
*/
public ArrayDeque<E> clone() {
try {
+ @SuppressWarnings("unchecked")
ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
result.elements = Arrays.copyOf(elements, elements.length);
return result;
@@ -806,7 +811,8 @@ public class ArrayDeque<E> extends AbstractCollection<E>
* followed by all of its elements (each an object reference) in
* first-to-last order.
*/
- private void writeObject(ObjectOutputStream s) throws IOException {
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
s.defaultWriteObject();
// Write out size
@@ -821,8 +827,8 @@ public class ArrayDeque<E> extends AbstractCollection<E>
/**
* Deserialize this deque.
*/
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Read in size and allocate array
@@ -833,6 +839,6 @@ public class ArrayDeque<E> extends AbstractCollection<E>
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
- elements[i] = (E)s.readObject();
+ elements[i] = s.readObject();
}
}
diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java
index 9d0f4a4..4a149b7 100644
--- a/luni/src/main/java/java/util/Arrays.java
+++ b/luni/src/main/java/java/util/Arrays.java
@@ -35,7 +35,7 @@ public class Arrays {
ArrayList(E[] storage) {
if (storage == null) {
- throw new NullPointerException();
+ throw new NullPointerException("storage == null");
}
a = storage;
}
@@ -2459,7 +2459,7 @@ public class Arrays {
*/
public static boolean[] copyOf(boolean[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2478,7 +2478,7 @@ public class Arrays {
*/
public static byte[] copyOf(byte[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2497,7 +2497,7 @@ public class Arrays {
*/
public static char[] copyOf(char[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2516,7 +2516,7 @@ public class Arrays {
*/
public static double[] copyOf(double[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2535,7 +2535,7 @@ public class Arrays {
*/
public static float[] copyOf(float[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2554,7 +2554,7 @@ public class Arrays {
*/
public static int[] copyOf(int[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2573,7 +2573,7 @@ public class Arrays {
*/
public static long[] copyOf(long[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2592,7 +2592,7 @@ public class Arrays {
*/
public static short[] copyOf(short[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2611,10 +2611,10 @@ public class Arrays {
*/
public static <T> T[] copyOf(T[] original, int newLength) {
if (original == null) {
- throw new NullPointerException();
+ throw new NullPointerException("original == null");
}
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2636,7 +2636,7 @@ public class Arrays {
public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
// We use the null pointer check in copyOfRange for exception priority compatibility.
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength, newType);
}
diff --git a/luni/src/main/java/java/util/BitSet.java b/luni/src/main/java/java/util/BitSet.java
index a4ee4c1..9dfe35e 100644
--- a/luni/src/main/java/java/util/BitSet.java
+++ b/luni/src/main/java/java/util/BitSet.java
@@ -85,7 +85,7 @@ public class BitSet implements Serializable, Cloneable {
*/
public BitSet(int bitCount) {
if (bitCount < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(bitCount));
}
this.bits = arrayForBits(bitCount);
this.longCount = 0;
diff --git a/luni/src/main/java/java/util/Calendar.java b/luni/src/main/java/java/util/Calendar.java
index bef6e26..81d01fb 100644
--- a/luni/src/main/java/java/util/Calendar.java
+++ b/luni/src/main/java/java/util/Calendar.java
@@ -877,8 +877,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
return getTimeInMillis() == cal.getTimeInMillis()
&& isLenient() == cal.isLenient()
&& getFirstDayOfWeek() == cal.getFirstDayOfWeek()
- && getMinimalDaysInFirstWeek() == cal
- .getMinimalDaysInFirstWeek()
+ && getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek()
&& getTimeZone().equals(cal.getTimeZone());
}
@@ -903,11 +902,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
}
/**
- * Gets the maximum value of the specified field for the current date.
- *
- * @param field
- * the field.
- * @return the maximum value of the specified field.
+ * Returns the maximum value of the specified field for the current date.
+ * For example, the maximum number of days in the current month.
*/
public int getActualMaximum(int field) {
int value, next;
@@ -1393,7 +1389,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
*/
public int compareTo(Calendar anotherCalendar) {
if (anotherCalendar == null) {
- throw new NullPointerException();
+ throw new NullPointerException("anotherCalendar == null");
}
long timeInMillis = getTimeInMillis();
long anotherTimeInMillis = anotherCalendar.getTimeInMillis();
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index b6729b4..d49ca85 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -1412,7 +1412,7 @@ public class Collections {
@SuppressWarnings("unchecked")
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T object) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list.isEmpty()) {
return -1;
@@ -1916,7 +1916,7 @@ public class Collections {
@SuppressWarnings("unchecked")
public static void swap(List<?> list, int index1, int index2) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
final int size = list.size();
if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) {
@@ -2174,7 +2174,7 @@ public class Collections {
public static <T> Collection<T> synchronizedCollection(
Collection<T> collection) {
if (collection == null) {
- throw new NullPointerException();
+ throw new NullPointerException("collection == null");
}
return new SynchronizedCollection<T>(collection);
}
@@ -2189,7 +2189,7 @@ public class Collections {
*/
public static <T> List<T> synchronizedList(List<T> list) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list instanceof RandomAccess) {
return new SynchronizedRandomAccessList<T>(list);
@@ -2207,7 +2207,7 @@ public class Collections {
*/
public static <K, V> Map<K, V> synchronizedMap(Map<K, V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new SynchronizedMap<K, V>(map);
}
@@ -2222,7 +2222,7 @@ public class Collections {
*/
public static <E> Set<E> synchronizedSet(Set<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new SynchronizedSet<E>(set);
}
@@ -2238,7 +2238,7 @@ public class Collections {
public static <K, V> SortedMap<K, V> synchronizedSortedMap(
SortedMap<K, V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new SynchronizedSortedMap<K, V>(map);
}
@@ -2253,7 +2253,7 @@ public class Collections {
*/
public static <E> SortedSet<E> synchronizedSortedSet(SortedSet<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new SynchronizedSortedSet<E>(set);
}
@@ -2271,7 +2271,7 @@ public class Collections {
public static <E> Collection<E> unmodifiableCollection(
Collection<? extends E> collection) {
if (collection == null) {
- throw new NullPointerException();
+ throw new NullPointerException("collection == null");
}
return new UnmodifiableCollection<E>((Collection<E>) collection);
}
@@ -2288,7 +2288,7 @@ public class Collections {
@SuppressWarnings("unchecked")
public static <E> List<E> unmodifiableList(List<? extends E> list) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list instanceof RandomAccess) {
return new UnmodifiableRandomAccessList<E>((List<E>) list);
@@ -2309,7 +2309,7 @@ public class Collections {
public static <K, V> Map<K, V> unmodifiableMap(
Map<? extends K, ? extends V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new UnmodifiableMap<K, V>((Map<K, V>) map);
}
@@ -2326,7 +2326,7 @@ public class Collections {
@SuppressWarnings("unchecked")
public static <E> Set<E> unmodifiableSet(Set<? extends E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new UnmodifiableSet<E>((Set<E>) set);
}
@@ -2344,7 +2344,7 @@ public class Collections {
public static <K, V> SortedMap<K, V> unmodifiableSortedMap(
SortedMap<K, ? extends V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new UnmodifiableSortedMap<K, V>((SortedMap<K, V>) map);
}
@@ -2360,7 +2360,7 @@ public class Collections {
*/
public static <E> SortedSet<E> unmodifiableSortedSet(SortedSet<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new UnmodifiableSortedSet<E>(set);
}
@@ -2381,7 +2381,7 @@ public class Collections {
*/
public static int frequency(Collection<?> c, Object o) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
if (c.isEmpty()) {
return 0;
@@ -2834,8 +2834,10 @@ public class Collections {
Class<E> type;
public CheckedCollection(Collection<E> c, Class<E> type) {
- if (c == null || type == null) {
- throw new NullPointerException();
+ if (c == null) {
+ throw new NullPointerException("c == null");
+ } else if (type == null) {
+ throw new NullPointerException("type == null");
}
this.c = c;
this.type = type;
@@ -3079,8 +3081,12 @@ public class Collections {
Class<V> valueType;
private CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) {
- if (m == null || keyType == null || valueType == null) {
- throw new NullPointerException();
+ if (m == null) {
+ throw new NullPointerException("m == null");
+ } else if (keyType == null) {
+ throw new NullPointerException("keyType == null");
+ } else if (valueType == null) {
+ throw new NullPointerException("valueType == null");
}
this.m = m;
this.keyType = keyType;
@@ -3172,7 +3178,7 @@ public class Collections {
public CheckedEntry(Map.Entry<K, V> e, Class<V> valueType) {
if (e == null) {
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
}
this.e = e;
this.valueType = valueType;
diff --git a/luni/src/main/java/java/util/Deque.java b/luni/src/main/java/java/util/Deque.java
index cb6bd90..f74a6b4 100644
--- a/luni/src/main/java/java/util/Deque.java
+++ b/luni/src/main/java/java/util/Deque.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc
// END android-note
/**
@@ -356,7 +355,7 @@ public interface Deque<E> extends Queue<E> {
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
* <p>This method is equivalent to {@link #addLast}.
*
diff --git a/luni/src/main/java/java/util/DualPivotQuicksort.java b/luni/src/main/java/java/util/DualPivotQuicksort.java
index 97797d1..5d2f77f 100644
--- a/luni/src/main/java/java/util/DualPivotQuicksort.java
+++ b/luni/src/main/java/java/util/DualPivotQuicksort.java
@@ -1565,7 +1565,7 @@ final class DualPivotQuicksort {
for (int k = left; k <= n; k++) {
float ak = a[k];
- if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToIntBits(ak)) {
+ if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToRawIntBits(ak)) {
a[k] = 0.0f;
numNegativeZeros++;
} else if (ak != ak) { // i.e., ak is NaN
@@ -1938,7 +1938,7 @@ final class DualPivotQuicksort {
for (int k = left; k <= n; k++) {
double ak = a[k];
- if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToLongBits(ak)) {
+ if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToRawLongBits(ak)) {
a[k] = 0.0d;
numNegativeZeros++;
} else if (ak != ak) { // i.e., ak is NaN
diff --git a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
index d04db8e..2a2bc2e 100644
--- a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
+++ b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
@@ -37,7 +37,7 @@ public class DuplicateFormatFlagsException extends IllegalFormatException {
*/
public DuplicateFormatFlagsException(String f) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
flags = f;
}
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index d3d42b4..a721ee3 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -773,7 +773,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@SuppressWarnings("unchecked")
private V putImpl(K key, V value) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
keyType.cast(key); // Called to throw ClassCastException.
int keyOrdinal = key.ordinal();
diff --git a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
index 5792877..5c36788 100644
--- a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
+++ b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
@@ -44,7 +44,7 @@ public class FormatFlagsConversionMismatchException extends
*/
public FormatFlagsConversionMismatchException(String f, char c) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
this.f = f;
this.c = c;
diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java
index e9a2f4a..021da08 100644
--- a/luni/src/main/java/java/util/Formatter.java
+++ b/luni/src/main/java/java/util/Formatter.java
@@ -884,7 +884,7 @@ public final class Formatter implements Closeable, Flushable {
*/
public Formatter(PrintStream ps) {
if (ps == null) {
- throw new NullPointerException();
+ throw new NullPointerException("ps == null");
}
out = ps;
locale = Locale.getDefault();
diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java
index c0fd521..9ff9ccc 100644
--- a/luni/src/main/java/java/util/GregorianCalendar.java
+++ b/luni/src/main/java/java/util/GregorianCalendar.java
@@ -219,14 +219,6 @@ public class GregorianCalendar extends Calendar {
private static int[] leastMaximums = new int[] { 1, 292269054, 11, 50, 3,
28, 355, 7, 3, 1, 11, 23, 59, 59, 999, 50400000, 1200000 };
- private boolean isCached;
-
- private int[] cachedFields = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- private long nextMidnightMillis = 0L;
-
- private long lastMidnightMillis = 0L;
-
private int currentYearSkew = 10;
private int lastYearSkew = 0;
@@ -365,8 +357,6 @@ public class GregorianCalendar extends Calendar {
throw new IllegalArgumentException();
}
- isCached = false;
-
if (field == ERA) {
complete();
if (fields[ERA] == AD) {
@@ -468,19 +458,8 @@ public class GregorianCalendar extends Calendar {
complete();
}
- /**
- * Creates new instance of {@code GregorianCalendar} with the same properties.
- *
- * @return a shallow copy of this {@code GregorianCalendar}.
- */
- @Override
- public Object clone() {
- GregorianCalendar thisClone = (GregorianCalendar) super.clone();
- thisClone.cachedFields = cachedFields.clone();
- return thisClone;
- }
-
- private final void fullFieldsCalc(long timeVal, int millis, int zoneOffset) {
+ private void fullFieldsCalc(long timeVal, int zoneOffset) {
+ int millis = (int) (time % 86400000);
long days = timeVal / 86400000;
if (millis < 0) {
@@ -583,31 +562,6 @@ public class GregorianCalendar extends Calendar {
}
}
- private final void cachedFieldsCheckAndGet(long timeVal,
- long newTimeMillis, long newTimeMillisAdjusted, int millis,
- int zoneOffset) {
- int dstOffset = fields[DST_OFFSET];
- if (!isCached
- || newTimeMillis >= nextMidnightMillis
- || newTimeMillis <= lastMidnightMillis
- || cachedFields[4] != zoneOffset
- || (dstOffset == 0 && (newTimeMillisAdjusted >= nextMidnightMillis))
- || (dstOffset != 0 && (newTimeMillisAdjusted <= lastMidnightMillis))) {
- fullFieldsCalc(timeVal, millis, zoneOffset);
- isCached = false;
- } else {
- fields[YEAR] = cachedFields[0];
- fields[MONTH] = cachedFields[1];
- fields[DATE] = cachedFields[2];
- fields[DAY_OF_WEEK] = cachedFields[3];
- fields[ERA] = cachedFields[5];
- fields[WEEK_OF_YEAR] = cachedFields[6];
- fields[WEEK_OF_MONTH] = cachedFields[7];
- fields[DAY_OF_YEAR] = cachedFields[8];
- fields[DAY_OF_WEEK_IN_MONTH] = cachedFields[9];
- }
- }
-
@Override
protected void computeFields() {
TimeZone timeZone = getTimeZone();
@@ -616,99 +570,11 @@ public class GregorianCalendar extends Calendar {
fields[DST_OFFSET] = dstOffset;
fields[ZONE_OFFSET] = zoneOffset;
- int millis = (int) (time % 86400000);
- int savedMillis = millis;
- // compute without a change in daylight saving time
- int offset = zoneOffset + dstOffset;
- long newTime = time + offset;
-
- if (time > 0L && newTime < 0L && offset > 0) {
- newTime = 0x7fffffffffffffffL;
- } else if (time < 0L && newTime > 0L && offset < 0) {
- newTime = 0x8000000000000000L;
- }
-
- // FIXME: I don't think this caching ever really gets used, because it requires that the
- // time zone doesn't use daylight savings (ever). So unless you're somewhere like Taiwan...
- if (isCached) {
- if (millis < 0) {
- millis += 86400000;
- }
-
- // Cannot add ZONE_OFFSET to time as it might overflow
- millis += zoneOffset;
- millis += dstOffset;
-
- if (millis < 0) {
- millis += 86400000;
- } else if (millis >= 86400000) {
- millis -= 86400000;
- }
-
- fields[MILLISECOND] = (millis % 1000);
- millis /= 1000;
- fields[SECOND] = (millis % 60);
- millis /= 60;
- fields[MINUTE] = (millis % 60);
- millis /= 60;
- fields[HOUR_OF_DAY] = (millis % 24);
- millis /= 24;
- fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
- fields[HOUR] = fields[HOUR_OF_DAY] % 12;
-
- // FIXME: this has to be wrong; useDaylightTime doesn't mean what they think it means!
- long newTimeAdjusted = newTime;
- if (timeZone.useDaylightTime()) {
- int dstSavings = timeZone.getDSTSavings();
- newTimeAdjusted += (dstOffset == 0) ? dstSavings : -dstSavings;
- }
-
- if (newTime > 0L && newTimeAdjusted < 0L && dstOffset == 0) {
- newTimeAdjusted = 0x7fffffffffffffffL;
- } else if (newTime < 0L && newTimeAdjusted > 0L && dstOffset != 0) {
- newTimeAdjusted = 0x8000000000000000L;
- }
-
- cachedFieldsCheckAndGet(time, newTime, newTimeAdjusted,
- savedMillis, zoneOffset);
- } else {
- fullFieldsCalc(time, savedMillis, zoneOffset);
- }
+ fullFieldsCalc(time, zoneOffset);
for (int i = 0; i < FIELD_COUNT; i++) {
isSet[i] = true;
}
-
- // Caching
- if (!isCached
- && newTime != 0x7fffffffffffffffL
- && newTime != 0x8000000000000000L
- && (!timeZone.useDaylightTime() || timeZone instanceof SimpleTimeZone)) {
- int cacheMillis = 0;
-
- cachedFields[0] = fields[YEAR];
- cachedFields[1] = fields[MONTH];
- cachedFields[2] = fields[DATE];
- cachedFields[3] = fields[DAY_OF_WEEK];
- cachedFields[4] = zoneOffset;
- cachedFields[5] = fields[ERA];
- cachedFields[6] = fields[WEEK_OF_YEAR];
- cachedFields[7] = fields[WEEK_OF_MONTH];
- cachedFields[8] = fields[DAY_OF_YEAR];
- cachedFields[9] = fields[DAY_OF_WEEK_IN_MONTH];
-
- cacheMillis += (23 - fields[HOUR_OF_DAY]) * 60 * 60 * 1000;
- cacheMillis += (59 - fields[MINUTE]) * 60 * 1000;
- cacheMillis += (59 - fields[SECOND]) * 1000;
- nextMidnightMillis = newTime + cacheMillis;
-
- cacheMillis = fields[HOUR_OF_DAY] * 60 * 60 * 1000;
- cacheMillis += fields[MINUTE] * 60 * 1000;
- cacheMillis += fields[SECOND] * 1000;
- lastMidnightMillis = newTime - cacheMillis;
-
- isCached = true;
- }
}
@Override
@@ -939,19 +805,17 @@ public class GregorianCalendar extends Calendar {
return (int) days + 1;
}
- private long daysFromBaseYear(int iyear) {
- long year = iyear;
-
+ private long daysFromBaseYear(long year) {
if (year >= 1970) {
long days = (year - 1970) * 365 + ((year - 1969) / 4);
if (year > changeYear) {
days -= ((year - 1901) / 100) - ((year - 1601) / 400);
} else {
- if(year == changeYear){
+ if (year == changeYear) {
days += currentYearSkew;
- }else if(year == changeYear -1){
+ } else if (year == changeYear - 1) {
days += lastYearSkew;
- }else{
+ } else {
days += julianSkew;
}
}
@@ -995,21 +859,10 @@ public class GregorianCalendar extends Calendar {
}
/**
- * Compares the specified {@code Object} to this {@code GregorianCalendar} and returns whether
- * they are equal. To be equal, the {@code Object} must be an instance of {@code GregorianCalendar} and
- * have the same properties.
- *
- * @param object
- * the {@code Object} to compare with this {@code GregorianCalendar}.
- * @return {@code true} if {@code object} is equal to this
- * {@code GregorianCalendar}, {@code false} otherwise.
- * @throws IllegalArgumentException
- * if the time is not set and the time cannot be computed
- * from the current field values.
- * @see #hashCode
+ * Returns true if {@code object} is a GregorianCalendar with the same
+ * properties.
*/
- @Override
- public boolean equals(Object object) {
+ @Override public boolean equals(Object object) {
if (!(object instanceof GregorianCalendar)) {
return false;
}
@@ -1020,28 +873,12 @@ public class GregorianCalendar extends Calendar {
&& gregorianCutover == ((GregorianCalendar) object).gregorianCutover;
}
- /**
- * Gets the maximum value of the specified field for the current date. For
- * example, the maximum number of days in the current month.
- *
- * @param field
- * the field.
- * @return the maximum value of the specified field.
- */
- @Override
- public int getActualMaximum(int field) {
+ @Override public int getActualMaximum(int field) {
int value;
if ((value = maximums[field]) == leastMaximums[field]) {
return value;
}
- switch (field) {
- case WEEK_OF_YEAR:
- case WEEK_OF_MONTH:
- isCached = false;
- break;
- }
-
complete();
long orgTime = time;
int result = 0;
@@ -1216,32 +1053,16 @@ public class GregorianCalendar extends Calendar {
month++;
}
int dayOfWeek = mod7(dayCount - 3) + 1;
- int offset = timeZone.getOffset(AD, year, month, date, dayOfWeek,
- millis);
- return offset;
+ return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis);
}
- /**
- * Returns an integer hash code for the receiver. Objects which are equal
- * return the same value for this method.
- *
- * @return the receiver's hash.
- *
- * @see #equals
- */
- @Override
- public int hashCode() {
+ @Override public int hashCode() {
return super.hashCode()
+ ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover);
}
/**
- * Returns whether the specified year is a leap year.
- *
- * @param year
- * the year.
- * @return {@code true} if the specified year is a leap year, {@code false}
- * otherwise.
+ * Returns true if {@code year} is a leap year.
*/
public boolean isLeapYear(int year) {
if (year > changeYear) {
@@ -1294,8 +1115,6 @@ public class GregorianCalendar extends Calendar {
throw new IllegalArgumentException();
}
- isCached = false;
-
complete();
int days, day, mod, maxWeeks, newWeek;
int max = -1;
@@ -1410,13 +1229,10 @@ public class GregorianCalendar extends Calendar {
/**
* Sets the gregorian change date of this calendar.
- *
- * @param date
- * a {@code Date} which represents the gregorian change date.
*/
public void setGregorianChange(Date date) {
gregorianCutover = date.getTime();
- GregorianCalendar cal = new GregorianCalendar(TimeZone.GMT);
+ GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
changeYear = cal.get(YEAR);
if (cal.get(ERA) == BC) {
@@ -1424,7 +1240,6 @@ public class GregorianCalendar extends Calendar {
}
julianSkew = ((changeYear - 2000) / 400) + julianError()
- ((changeYear - 2000) / 100);
- isCached = false;
int dayOfYear = cal.get(DAY_OF_YEAR);
if (dayOfYear < julianSkew) {
currentYearSkew = dayOfYear-1;
@@ -1433,30 +1248,14 @@ public class GregorianCalendar extends Calendar {
lastYearSkew = 0;
currentYearSkew = julianSkew;
}
- isCached = false;
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
}
- private void readObject(ObjectInputStream stream) throws IOException,
- ClassNotFoundException {
-
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
setGregorianChange(new Date(gregorianCutover));
- isCached = false;
- }
-
- @Override
- public void setFirstDayOfWeek(int value) {
- super.setFirstDayOfWeek(value);
- isCached = false;
- }
-
- @Override
- public void setMinimalDaysInFirstWeek(int value) {
- super.setMinimalDaysInFirstWeek(value);
- isCached = false;
}
}
diff --git a/luni/src/main/java/java/util/Hashtable.java b/luni/src/main/java/java/util/Hashtable.java
index cea29da..a4e24bc 100644
--- a/luni/src/main/java/java/util/Hashtable.java
+++ b/luni/src/main/java/java/util/Hashtable.java
@@ -313,7 +313,7 @@ public class Hashtable<K, V> extends Dictionary<K, V>
*/
public synchronized boolean containsValue(Object value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
HashtableEntry[] tab = table;
@@ -361,8 +361,10 @@ public class Hashtable<K, V> extends Dictionary<K, V>
* @see java.lang.Object#equals
*/
public synchronized V put(K key, V value) {
- if (value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
int hash = secondaryHash(key.hashCode());
HashtableEntry<K, V>[] tab = table;
@@ -395,8 +397,10 @@ public class Hashtable<K, V> extends Dictionary<K, V>
* ensure that capacity is sufficient, and does not increment modCount.
*/
private void constructorPut(K key, V value) {
- if (value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
int hash = secondaryHash(key.hashCode());
HashtableEntry<K, V>[] tab = table;
@@ -680,7 +684,7 @@ public class Hashtable<K, V> extends Dictionary<K, V>
public final V setValue(V value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
V oldValue = this.value;
this.value = value;
diff --git a/luni/src/main/java/java/util/IllegalFormatConversionException.java b/luni/src/main/java/java/util/IllegalFormatConversionException.java
index 31c0eed..af986f6 100644
--- a/luni/src/main/java/java/util/IllegalFormatConversionException.java
+++ b/luni/src/main/java/java/util/IllegalFormatConversionException.java
@@ -46,7 +46,7 @@ public class IllegalFormatConversionException extends IllegalFormatException
public IllegalFormatConversionException(char c, Class<?> arg) {
this.c = c;
if (arg == null) {
- throw new NullPointerException();
+ throw new NullPointerException("arg == null");
}
this.arg = arg;
}
diff --git a/luni/src/main/java/java/util/IllegalFormatFlagsException.java b/luni/src/main/java/java/util/IllegalFormatFlagsException.java
index 6947912..4946c55 100644
--- a/luni/src/main/java/java/util/IllegalFormatFlagsException.java
+++ b/luni/src/main/java/java/util/IllegalFormatFlagsException.java
@@ -38,7 +38,7 @@ public class IllegalFormatFlagsException extends IllegalFormatException implemen
*/
public IllegalFormatFlagsException(String flags) {
if (flags == null) {
- throw new NullPointerException();
+ throw new NullPointerException("flags == null");
}
this.flags = flags;
}
diff --git a/luni/src/main/java/java/util/ListResourceBundle.java b/luni/src/main/java/java/util/ListResourceBundle.java
index 1508b93..fc6ab97 100644
--- a/luni/src/main/java/java/util/ListResourceBundle.java
+++ b/luni/src/main/java/java/util/ListResourceBundle.java
@@ -108,7 +108,7 @@ public abstract class ListResourceBundle extends ResourceBundle {
public final Object handleGetObject(String key) {
initializeTable();
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
return table.get(key);
}
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 6b20a1c..0fbe2f5 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -396,6 +396,17 @@ public final class Locale implements Cloneable, Serializable {
if (languageCode.isEmpty()) {
return "";
}
+
+ // Last-minute workaround for http://b/7291355 in jb-mr1.
+ // This isn't right for all languages, but it's right for en and tl.
+ // We should have more CLDR data in a future release, but we'll still
+ // probably want to have frameworks/base translate the obsolete tl and
+ // tl-rPH locales to fil and fil-rPH at runtime, at which point
+ // libcore and icu4c will just do the right thing.
+ if (languageCode.equals("tl")) {
+ return "Filipino";
+ }
+
String result = ICU.getDisplayLanguageNative(toString(), locale.toString());
if (result == null) { // TODO: do we need to do this, or does ICU do it for us?
result = ICU.getDisplayLanguageNative(toString(), Locale.getDefault().toString());
diff --git a/luni/src/main/java/java/util/MissingFormatArgumentException.java b/luni/src/main/java/java/util/MissingFormatArgumentException.java
index ce72efa..1733501 100644
--- a/luni/src/main/java/java/util/MissingFormatArgumentException.java
+++ b/luni/src/main/java/java/util/MissingFormatArgumentException.java
@@ -37,7 +37,7 @@ public class MissingFormatArgumentException extends IllegalFormatException {
*/
public MissingFormatArgumentException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/MissingFormatWidthException.java b/luni/src/main/java/java/util/MissingFormatWidthException.java
index b6d0ca6..0a3b5ae 100644
--- a/luni/src/main/java/java/util/MissingFormatWidthException.java
+++ b/luni/src/main/java/java/util/MissingFormatWidthException.java
@@ -36,7 +36,7 @@ public class MissingFormatWidthException extends IllegalFormatException {
*/
public MissingFormatWidthException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/NavigableMap.java b/luni/src/main/java/java/util/NavigableMap.java
index 29961c8..beeb651 100644
--- a/luni/src/main/java/java/util/NavigableMap.java
+++ b/luni/src/main/java/java/util/NavigableMap.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of JCP
* JSR-166 Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #subMap(Object)} to {@link #subMap} to satisfy DroidDoc
// END android-note
/**
@@ -48,9 +47,9 @@ package java.util;
* method {@code put}.
*
* <p>Methods
- * {@link #subMap subMap(K, K)},
- * {@link #headMap headMap(K)}, and
- * {@link #tailMap tailMap(K)}
+ * {@link #subMap(Object, Object) subMap(K, K)},
+ * {@link #headMap(Object) headMap(K)}, and
+ * {@link #tailMap(Object) tailMap(K)}
* are specified to return {@code SortedMap} to allow existing
* implementations of {@code SortedMap} to be compatibly retrofitted to
* implement {@code NavigableMap}, but extensions and implementations
diff --git a/luni/src/main/java/java/util/NavigableSet.java b/luni/src/main/java/java/util/NavigableSet.java
index cff0800..f410313 100644
--- a/luni/src/main/java/java/util/NavigableSet.java
+++ b/luni/src/main/java/java/util/NavigableSet.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of JCP
* JSR-166 Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #subSet(Object)} to {@link #subSet} to satisfy DroidDoc
// END android-note
/**
@@ -41,9 +40,9 @@ package java.util;
* Comparable} elements intrinsically do not permit {@code null}.)
*
* <p>Methods
- * {@link #subSet subSet(E, E)},
- * {@link #headSet headSet(E)}, and
- * {@link #tailSet tailSet(E)}
+ * {@link #subSet(Object, Object) subSet(E, E)},
+ * {@link #headSet(Object) headSet(E)}, and
+ * {@link #tailSet(Object) tailSet(E)}
* are specified to return {@code SortedSet} to allow existing
* implementations of {@code SortedSet} to be compatibly retrofitted to
* implement {@code NavigableSet}, but extensions and implementations
diff --git a/luni/src/main/java/java/util/Observable.java b/luni/src/main/java/java/util/Observable.java
index 2c2877e..c984c68 100644
--- a/luni/src/main/java/java/util/Observable.java
+++ b/luni/src/main/java/java/util/Observable.java
@@ -49,7 +49,7 @@ public class Observable {
*/
public void addObserver(Observer observer) {
if (observer == null) {
- throw new NullPointerException();
+ throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
diff --git a/luni/src/main/java/java/util/PriorityQueue.java b/luni/src/main/java/java/util/PriorityQueue.java
index 10c5968..e09eb05 100644
--- a/luni/src/main/java/java/util/PriorityQueue.java
+++ b/luni/src/main/java/java/util/PriorityQueue.java
@@ -186,7 +186,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable {
*/
public boolean offer(E o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
growToSize(size + 1);
elements[size] = o;
@@ -387,7 +387,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable {
private void initSize(Collection<? extends E> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
if (c.isEmpty()) {
elements = newElementArray(1);
diff --git a/luni/src/main/java/java/util/Properties.java b/luni/src/main/java/java/util/Properties.java
index 1731ad8..57c6a00 100644
--- a/luni/src/main/java/java/util/Properties.java
+++ b/luni/src/main/java/java/util/Properties.java
@@ -243,7 +243,7 @@ public class Properties extends Hashtable<Object, Object> {
*/
public synchronized void load(InputStream in) throws IOException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
load(new InputStreamReader(in, "ISO-8859-1"));
}
@@ -276,7 +276,7 @@ public class Properties extends Hashtable<Object, Object> {
@SuppressWarnings("fallthrough")
public synchronized void load(Reader in) throws IOException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
int mode = NONE, unicode = 0, count = 0;
char nextChar, buf[] = new char[40];
@@ -578,7 +578,7 @@ public class Properties extends Hashtable<Object, Object> {
public synchronized void loadFromXML(InputStream in) throws IOException,
InvalidPropertiesFormatException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
if (builder == null) {
@@ -690,8 +690,10 @@ public class Properties extends Hashtable<Object, Object> {
public synchronized void storeToXML(OutputStream os, String comment,
String encoding) throws IOException {
- if (os == null || encoding == null) {
- throw new NullPointerException();
+ if (os == null) {
+ throw new NullPointerException("os == null");
+ } else if (encoding == null) {
+ throw new NullPointerException("encoding == null");
}
/*
diff --git a/luni/src/main/java/java/util/PropertyResourceBundle.java b/luni/src/main/java/java/util/PropertyResourceBundle.java
index 4029ee1..dbbd139 100644
--- a/luni/src/main/java/java/util/PropertyResourceBundle.java
+++ b/luni/src/main/java/java/util/PropertyResourceBundle.java
@@ -46,7 +46,7 @@ public class PropertyResourceBundle extends ResourceBundle {
*/
public PropertyResourceBundle(InputStream stream) throws IOException {
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
resources = new Properties();
resources.load(stream);
diff --git a/luni/src/main/java/java/util/Queue.java b/luni/src/main/java/java/util/Queue.java
index 5aef944..8b465e6 100644
--- a/luni/src/main/java/java/util/Queue.java
+++ b/luni/src/main/java/java/util/Queue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
diff --git a/luni/src/main/java/java/util/Random.java b/luni/src/main/java/java/util/Random.java
index 7ce74cc..b0a92ff 100644
--- a/luni/src/main/java/java/util/Random.java
+++ b/luni/src/main/java/java/util/Random.java
@@ -140,25 +140,23 @@ public class Random implements Serializable {
* section 3.4.1, subsection C, algorithm P.
*/
public synchronized double nextGaussian() {
- if (haveNextNextGaussian) { // if X1 has been returned, return the
- // second Gaussian
+ if (haveNextNextGaussian) {
haveNextNextGaussian = false;
return nextNextGaussian;
}
double v1, v2, s;
do {
- v1 = 2 * nextDouble() - 1; // Generates two independent random
- // variables U1, U2
+ v1 = 2 * nextDouble() - 1;
v2 = 2 * nextDouble() - 1;
s = v1 * v1 + v2 * v2;
- } while (s >= 1);
- double norm = Math.sqrt(-2 * Math.log(s) / s);
- nextNextGaussian = v2 * norm; // should that not be norm instead
- // of multiplier ?
+ } while (s >= 1 || s == 0);
+
+ // The specification says this uses StrictMath.
+ double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
+ nextNextGaussian = v2 * multiplier;
haveNextNextGaussian = true;
- return v1 * norm; // should that not be norm instead of multiplier
- // ?
+ return v1 * multiplier;
}
/**
diff --git a/luni/src/main/java/java/util/ResourceBundle.java b/luni/src/main/java/java/util/ResourceBundle.java
index ff38b5b..f5c8285 100644
--- a/luni/src/main/java/java/util/ResourceBundle.java
+++ b/luni/src/main/java/java/util/ResourceBundle.java
@@ -211,8 +211,10 @@ public abstract class ResourceBundle {
*/
public static ResourceBundle getBundle(String bundleName, Locale locale,
ClassLoader loader) throws MissingResourceException {
- if (loader == null || bundleName == null) {
- throw new NullPointerException();
+ if (loader == null) {
+ throw new NullPointerException("loader == null");
+ } else if (bundleName == null) {
+ throw new NullPointerException("bundleName == null");
}
Locale defaultLocale = Locale.getDefault();
if (!cacheLocale.equals(defaultLocale)) {
@@ -610,14 +612,14 @@ public abstract class ResourceBundle {
public static void clearCache(ClassLoader loader) {
if (loader == null) {
- throw new NullPointerException();
+ throw new NullPointerException("loader == null");
}
cache.remove(loader);
}
public boolean containsKey(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
return keySet().contains(key);
}
@@ -665,8 +667,10 @@ public abstract class ResourceBundle {
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
return null;
}
@@ -804,8 +808,10 @@ public abstract class ResourceBundle {
* {@code locale}.
*/
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
List<Locale> retList = new ArrayList<Locale>();
String language = locale.getLanguage();
@@ -829,7 +835,7 @@ public abstract class ResourceBundle {
*/
public List<String> getFormats(String baseName) {
if (baseName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("baseName == null");
}
return format;
}
@@ -838,8 +844,10 @@ public abstract class ResourceBundle {
* Returns the fallback locale for {@code baseName} in {@code locale}.
*/
public Locale getFallbackLocale(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
if (Locale.getDefault() != locale) {
return Locale.getDefault();
@@ -872,8 +880,10 @@ public abstract class ResourceBundle {
String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException,
IOException {
- if (format == null || loader == null) {
- throw new NullPointerException();
+ if (format == null) {
+ throw new NullPointerException("format == null");
+ } else if (loader == null) {
+ throw new NullPointerException("loader == null");
}
final String bundleName = toBundleName(baseName, locale);
final ClassLoader clsloader = loader;
@@ -938,8 +948,10 @@ public abstract class ResourceBundle {
* default is TTL_NO_EXPIRATION_CONTROL.
*/
public long getTimeToLive(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
return TTL_NO_EXPIRATION_CONTROL;
}
@@ -966,7 +978,7 @@ public abstract class ResourceBundle {
long loadTime) {
if (bundle == null) {
// FIXME what's the use of bundle?
- throw new NullPointerException();
+ throw new NullPointerException("bundle == null");
}
String bundleName = toBundleName(baseName, locale);
String suffix = format;
@@ -1004,7 +1016,7 @@ public abstract class ResourceBundle {
final String preString = UNDER_SCORE;
final String underline = UNDER_SCORE;
if (baseName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("baseName == null");
}
StringBuilder ret = new StringBuilder();
StringBuilder prefix = new StringBuilder();
@@ -1044,7 +1056,7 @@ public abstract class ResourceBundle {
*/
public final String toResourceName(String bundleName, String suffix) {
if (suffix == null) {
- throw new NullPointerException();
+ throw new NullPointerException("suffix == null");
}
StringBuilder ret = new StringBuilder(bundleName.replace('.', '/'));
ret.append('.');
diff --git a/luni/src/main/java/java/util/Scanner.java b/luni/src/main/java/java/util/Scanner.java
index 8f889b3..5f7d0e3 100644
--- a/luni/src/main/java/java/util/Scanner.java
+++ b/luni/src/main/java/java/util/Scanner.java
@@ -241,7 +241,7 @@ public final class Scanner implements Iterator<String> {
*/
public Scanner(Readable src) {
if (src == null) {
- throw new NullPointerException();
+ throw new NullPointerException("src == null");
}
input = src;
initialization();
@@ -1664,7 +1664,7 @@ public final class Scanner implements Iterator<String> {
*/
public Scanner useLocale(Locale l) {
if (l == null) {
- throw new NullPointerException();
+ throw new NullPointerException("l == null");
}
this.locale = l;
return this;
@@ -1724,7 +1724,7 @@ public final class Scanner implements Iterator<String> {
*/
private void checkNull(Pattern pattern) {
if (pattern == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pattern == null");
}
}
diff --git a/luni/src/main/java/java/util/ServiceLoader.java b/luni/src/main/java/java/util/ServiceLoader.java
index beacaab..016ab3f 100644
--- a/luni/src/main/java/java/util/ServiceLoader.java
+++ b/luni/src/main/java/java/util/ServiceLoader.java
@@ -78,7 +78,7 @@ public final class ServiceLoader<S> implements Iterable<S> {
// It makes no sense for service to be null.
// classLoader is null if you want the system class loader.
if (service == null) {
- throw new NullPointerException();
+ throw new NullPointerException("service == null");
}
this.service = service;
this.classLoader = classLoader;
diff --git a/luni/src/main/java/java/util/SimpleTimeZone.java b/luni/src/main/java/java/util/SimpleTimeZone.java
index 6c9b443..93dc88e 100644
--- a/luni/src/main/java/java/util/SimpleTimeZone.java
+++ b/luni/src/main/java/java/util/SimpleTimeZone.java
@@ -217,10 +217,17 @@ public class SimpleTimeZone extends TimeZone {
throw new IllegalArgumentException("Invalid daylightSavings: " + daylightSavings);
}
dstSavings = daylightSavings;
- // TODO: do we need to set useDaylight is dstSavings != 0?
- setStartRule(startMonth, startDay, startDayOfWeek, startTime);
- setEndRule(endMonth, endDay, endDayOfWeek, endTime);
+ this.startMonth = startMonth;
+ this.startDay = startDay;
+ this.startDayOfWeek = startDayOfWeek;
+ this.startTime = startTime;
+ setStartMode();
+ this.endMonth = endMonth;
+ this.endDay = endDay;
+ this.endDayOfWeek = endDayOfWeek;
+ this.endTime = endTime;
+ setEndMode();
}
/**
diff --git a/luni/src/main/java/java/util/StringTokenizer.java b/luni/src/main/java/java/util/StringTokenizer.java
index 1b07604..e6686e8 100644
--- a/luni/src/main/java/java/util/StringTokenizer.java
+++ b/luni/src/main/java/java/util/StringTokenizer.java
@@ -91,13 +91,13 @@ public class StringTokenizer implements Enumeration<Object> {
*/
public StringTokenizer(String string, String delimiters,
boolean returnDelimiters) {
- if (string != null) {
- this.string = string;
- this.delimiters = delimiters;
- this.returnDelimiters = returnDelimiters;
- this.position = 0;
- } else
- throw new NullPointerException();
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+ this.string = string;
+ this.delimiters = delimiters;
+ this.returnDelimiters = returnDelimiters;
+ this.position = 0;
}
/**
@@ -143,7 +143,7 @@ public class StringTokenizer implements Enumeration<Object> {
*/
public boolean hasMoreTokens() {
if (delimiters == null) {
- throw new NullPointerException();
+ throw new NullPointerException("delimiters == null");
}
int length = string.length();
if (position < length) {
@@ -180,7 +180,7 @@ public class StringTokenizer implements Enumeration<Object> {
*/
public String nextToken() {
if (delimiters == null) {
- throw new NullPointerException();
+ throw new NullPointerException("delimiters == null");
}
int i = position;
int length = string.length();
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index 34763cc..85011bc 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -17,7 +17,10 @@
package java.util;
+import java.io.IOException;
import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import libcore.icu.TimeZones;
import libcore.util.ZoneInfoDB;
@@ -28,7 +31,7 @@ import libcore.util.ZoneInfoDB;
* <p>Most applications will use {@link #getDefault} which returns a {@code TimeZone} based on
* the time zone where the program is running.
*
- * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by id}.
+ * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by Olson ID}.
*
* <p>It is highly unlikely you'll ever want to use anything but the factory methods yourself.
* Let classes like {@link Calendar} and {@link java.text.SimpleDateFormat} do the date
@@ -64,6 +67,8 @@ import libcore.util.ZoneInfoDB;
public abstract class TimeZone implements Serializable, Cloneable {
private static final long serialVersionUID = 3581463369166924961L;
+ private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+
/**
* The short display name style, such as {@code PDT}. Requests for this
* style may yield GMT offsets like {@code GMT-08:00}.
@@ -76,7 +81,8 @@ public abstract class TimeZone implements Serializable, Cloneable {
*/
public static final int LONG = 1;
- static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); // Greenwich Mean Time
+ private static final TimeZone GMT = new SimpleTimeZone(0, "GMT");
+ private static final TimeZone UTC = new SimpleTimeZone(0, "UTC");
private static TimeZone defaultTimeZone;
@@ -214,16 +220,27 @@ public abstract class TimeZone implements Serializable, Cloneable {
}
/**
- * Returns the daylight savings offset in milliseconds for this time zone.
- * The base implementation returns {@code 3600000} (1 hour) for time zones
- * that use daylight savings time and {@code 0} for timezones that do not.
- * Subclasses should override this method for other daylight savings
- * offsets.
+ * Returns the latest daylight savings in milliseconds for this time zone, relative
+ * to this time zone's regular UTC offset (as returned by {@link #getRawOffset}).
+ *
+ * <p>This class returns {@code 3600000} (1 hour) for time zones
+ * that use daylight savings time and {@code 0} for timezones that do not,
+ * leaving it to subclasses to override this method for other daylight savings
+ * offsets. (There are time zones, such as {@code Australia/Lord_Howe},
+ * that use other values.)
*
- * <p>Note that this method doesn't tell you whether or not to apply the
+ * <p>Note that this method doesn't tell you whether or not to <i>apply</i> the
* offset: you need to call {@code inDaylightTime} for the specific time
* you're interested in. If this method returns a non-zero offset, that only
* tells you that this {@code TimeZone} sometimes observes daylight savings.
+ *
+ * <p>Note also that this method doesn't necessarily return the value you need
+ * to apply to the time you're working with. This value can and does change over
+ * time for a given time zone.
+ *
+ * <p>It's highly unlikely that you should ever call this method. You
+ * probably want {@link #getOffset} instead, which tells you the offset
+ * for a specific point in time, and takes daylight savings into account for you.
*/
public int getDSTSavings() {
return useDaylightTime() ? 3600000 : 0;
@@ -265,17 +282,19 @@ public abstract class TimeZone implements Serializable, Cloneable {
public abstract int getRawOffset();
/**
- * Returns a {@code TimeZone} suitable for {@code id}, or {@code GMT} for unknown ids.
+ * Returns a {@code TimeZone} corresponding to the given {@code id}, or {@code GMT}
+ * for unknown ids.
*
- * <p>An id can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
+ * <p>An ID can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
* as {@code America/Los_Angeles}. The {@link #getAvailableIDs} method returns
* the supported names.
*
- * <p>This method can also create a custom {@code TimeZone} using the following
- * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code TimeZone.getTimeZone("GMT+14:00")}
- * would return an object with a raw offset of +14 hours from UTC, and which does <i>not</i>
- * use daylight savings. These are rarely useful, because they don't correspond to time
- * zones actually in use.
+ * <p>This method can also create a custom {@code TimeZone} given an ID with the following
+ * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code "GMT+05:00"}, {@code "GMT+0500"},
+ * {@code "GMT+5:00"}, {@code "GMT+500"}, {@code "GMT+05"}, and {@code "GMT+5"} all return
+ * an object with a raw offset of +5 hours from UTC, and which does <i>not</i> use daylight
+ * savings. These are rarely useful, because they don't correspond to time zones actually
+ * in use by humans.
*
* <p>Other than the special cases "UTC" and "GMT" (which are synonymous in this context,
* both corresponding to UTC), Android does not support the deprecated three-letter time
@@ -285,80 +304,66 @@ public abstract class TimeZone implements Serializable, Cloneable {
if (id == null) {
throw new NullPointerException("id == null");
}
- TimeZone zone = ZoneInfoDB.getTimeZone(id);
- if (zone != null) {
- return zone;
+
+ // Special cases? These can clone an existing instance.
+ // TODO: should we just add a cache to ZoneInfoDB instead?
+ if (id.length() == 3) {
+ if (id.equals("GMT")) {
+ return (TimeZone) GMT.clone();
+ }
+ if (id.equals("UTC")) {
+ return (TimeZone) UTC.clone();
+ }
}
+
+ // In the database?
+ TimeZone zone = null;
+ try {
+ zone = ZoneInfoDB.makeTimeZone(id);
+ } catch (IOException ignored) {
+ }
+
+ // Custom time zone?
if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
zone = getCustomTimeZone(id);
}
- if (zone == null) {
- zone = (TimeZone) GMT.clone();
- }
- return zone;
+
+ // We never return null; on failure we return the equivalent of "GMT".
+ return (zone != null) ? zone : (TimeZone) GMT.clone();
}
/**
- * Returns a new SimpleTimeZone for an id of the form "GMT[+|-]hh[[:]mm]", or null.
+ * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
*/
private static TimeZone getCustomTimeZone(String id) {
- char sign = id.charAt(3);
- if (sign != '+' && sign != '-') {
- return null;
- }
- int[] position = new int[1];
- String formattedName = formatTimeZoneName(id, 4);
- int hour = parseNumber(formattedName, 4, position);
- if (hour < 0 || hour > 23) {
+ Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
+ if (!m.matches()) {
return null;
}
- int index = position[0];
- if (index == -1) {
- return null;
- }
- int raw = hour * 3600000;
- if (index < formattedName.length() && formattedName.charAt(index) == ':') {
- int minute = parseNumber(formattedName, index + 1, position);
- if (position[0] == -1 || minute < 0 || minute > 59) {
- return null;
- }
- raw += minute * 60000;
- } else if (hour >= 30 || index > 6) {
- raw = (hour / 100 * 3600000) + (hour % 100 * 60000);
- }
- if (sign == '-') {
- raw = -raw;
- }
- return new SimpleTimeZone(raw, formattedName);
- }
- private static String formatTimeZoneName(String name, int offset) {
- StringBuilder buf = new StringBuilder();
- int index = offset, length = name.length();
- buf.append(name.substring(0, offset));
-
- while (index < length) {
- if (Character.digit(name.charAt(index), 10) != -1) {
- buf.append(name.charAt(index));
- if ((length - (index + 1)) == 2) {
- buf.append(':');
- }
- } else if (name.charAt(index) == ':') {
- buf.append(':');
+ int hour;
+ int minute = 0;
+ try {
+ hour = Integer.parseInt(m.group(1));
+ if (m.group(3) != null) {
+ minute = Integer.parseInt(m.group(3));
}
- index++;
+ } catch (NumberFormatException impossible) {
+ throw new AssertionError(impossible);
}
- if (buf.toString().indexOf(":") == -1) {
- buf.append(':');
- buf.append("00");
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
+ return null;
}
- if (buf.toString().indexOf(":") == 5) {
- buf.insert(4, '0');
+ char sign = id.charAt(3);
+ int raw = (hour * 3600000) + (minute * 60000);
+ if (sign == '-') {
+ raw = -raw;
}
- return buf.toString();
+ String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
+ return new SimpleTimeZone(raw, cleanId);
}
/**
@@ -380,17 +385,6 @@ public abstract class TimeZone implements Serializable, Cloneable {
*/
public abstract boolean inDaylightTime(Date time);
- private static int parseNumber(String string, int offset, int[] position) {
- int index = offset, length = string.length(), digit, result = 0;
- while (index < length
- && (digit = Character.digit(string.charAt(index), 10)) != -1) {
- index++;
- result = result * 10 + digit;
- }
- position[0] = index == offset ? -1 : index;
- return result;
- }
-
/**
* Overrides the default time zone for the current process only.
*
@@ -411,7 +405,7 @@ public abstract class TimeZone implements Serializable, Cloneable {
*/
public void setID(String id) {
if (id == null) {
- throw new NullPointerException();
+ throw new NullPointerException("id == null");
}
ID = id;
}
diff --git a/luni/src/main/java/java/util/Timer.java b/luni/src/main/java/java/util/Timer.java
index b4f7a83..afc745c 100644
--- a/luni/src/main/java/java/util/Timer.java
+++ b/luni/src/main/java/java/util/Timer.java
@@ -362,7 +362,7 @@ public class Timer {
*/
public Timer(String name, boolean isDaemon) {
if (name == null) {
- throw new NullPointerException("name is null");
+ throw new NullPointerException("name == null");
}
this.impl = new TimerImpl(name, isDaemon);
this.finalizer = new FinalizerHelper(impl);
diff --git a/luni/src/main/java/java/util/UUID.java b/luni/src/main/java/java/util/UUID.java
index a932bb2..8ac0a63 100644
--- a/luni/src/main/java/java/util/UUID.java
+++ b/luni/src/main/java/java/util/UUID.java
@@ -142,7 +142,7 @@ public final class UUID implements Serializable, Comparable<UUID> {
*/
public static UUID nameUUIDFromBytes(byte[] name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
@@ -179,7 +179,7 @@ public final class UUID implements Serializable, Comparable<UUID> {
*/
public static UUID fromString(String uuid) {
if (uuid == null) {
- throw new NullPointerException();
+ throw new NullPointerException("uuid == null");
}
int[] position = new int[5];
diff --git a/luni/src/main/java/java/util/UnknownFormatConversionException.java b/luni/src/main/java/java/util/UnknownFormatConversionException.java
index e26de91..29af4e1 100644
--- a/luni/src/main/java/java/util/UnknownFormatConversionException.java
+++ b/luni/src/main/java/java/util/UnknownFormatConversionException.java
@@ -35,7 +35,7 @@ public class UnknownFormatConversionException extends IllegalFormatException {
*/
public UnknownFormatConversionException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/UnknownFormatFlagsException.java b/luni/src/main/java/java/util/UnknownFormatFlagsException.java
index 9daa3f1..990432b 100644
--- a/luni/src/main/java/java/util/UnknownFormatFlagsException.java
+++ b/luni/src/main/java/java/util/UnknownFormatFlagsException.java
@@ -37,7 +37,7 @@ public class UnknownFormatFlagsException extends IllegalFormatException {
*/
public UnknownFormatFlagsException(String f) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
flags = f;
}
diff --git a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
index 36fcecc..a7f7745 100644
--- a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
index a622832..e30ab67 100644
--- a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
@@ -1,12 +1,17 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
-import java.util.*;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.lang.ref.WeakReference;
// BEGIN android-note
// removed link to collections framework docs
@@ -73,11 +78,20 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
/** Main lock guarding all access */
final ReentrantLock lock;
+
/** Condition for waiting takes */
private final Condition notEmpty;
+
/** Condition for waiting puts */
private final Condition notFull;
+ /**
+ * Shared state for currently active iterators, or null if there
+ * are known not to be any. Allows queue operations to update
+ * iterator state.
+ */
+ transient Itrs itrs = null;
+
// Internal helper methods
/**
@@ -94,16 +108,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
return ((i == 0) ? items.length : i) - 1;
}
- @SuppressWarnings("unchecked")
- static <E> E cast(Object item) {
- return (E) item;
- }
-
/**
* Returns item at index i.
*/
final E itemAt(int i) {
- return this.<E>cast(items[i]);
+ @SuppressWarnings("unchecked") E x = (E) items[i];
+ return x;
}
/**
@@ -120,10 +130,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
- private void insert(E x) {
+ private void enqueue(E x) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[putIndex] == null;
items[putIndex] = x;
putIndex = inc(putIndex);
- ++count;
+ count++;
notEmpty.signal();
}
@@ -131,42 +143,57 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
- private E extract() {
+ private E dequeue() {
+ // assert lock.getHoldCount() == 1;
+ // assert items[takeIndex] != null;
final Object[] items = this.items;
- E x = this.<E>cast(items[takeIndex]);
+ @SuppressWarnings("unchecked") E x = (E) items[takeIndex];
items[takeIndex] = null;
takeIndex = inc(takeIndex);
- --count;
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
notFull.signal();
return x;
}
/**
- * Deletes item at position i.
- * Utility for remove and iterator.remove.
+ * Deletes item at array index removeIndex.
+ * Utility for remove(Object) and iterator.remove.
* Call only when holding lock.
*/
- void removeAt(int i) {
+ void removeAt(final int removeIndex) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[removeIndex] != null;
+ // assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
- // if removing front item, just advance
- if (i == takeIndex) {
+ if (removeIndex == takeIndex) {
+ // removing front item; just advance
items[takeIndex] = null;
takeIndex = inc(takeIndex);
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
} else {
+ // an "interior" remove
+
// slide over all others up through putIndex.
- for (;;) {
- int nexti = inc(i);
- if (nexti != putIndex) {
- items[i] = items[nexti];
- i = nexti;
+ final int putIndex = this.putIndex;
+ for (int i = removeIndex;;) {
+ int next = inc(i);
+ if (next != putIndex) {
+ items[i] = items[next];
+ i = next;
} else {
items[i] = null;
- putIndex = i;
+ this.putIndex = i;
break;
}
}
+ count--;
+ if (itrs != null)
+ itrs.removedAt(removeIndex);
}
- --count;
notFull.signal();
}
@@ -271,7 +298,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
if (count == items.length)
return false;
else {
- insert(e);
+ enqueue(e);
return true;
}
} finally {
@@ -293,7 +320,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
try {
while (count == items.length)
notFull.await();
- insert(e);
+ enqueue(e);
} finally {
lock.unlock();
}
@@ -320,7 +347,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
return false;
nanos = notFull.awaitNanos(nanos);
}
- insert(e);
+ enqueue(e);
return true;
} finally {
lock.unlock();
@@ -331,7 +358,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return (count == 0) ? null : extract();
+ return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
@@ -343,7 +370,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
try {
while (count == 0)
notEmpty.await();
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -359,7 +386,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
return null;
nanos = notEmpty.awaitNanos(nanos);
}
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -438,11 +465,15 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i])) {
+ removeAt(i);
+ return true;
+ }
+ } while ((i = inc(i)) != putIndex);
}
return false;
} finally {
@@ -464,9 +495,14 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- if (o.equals(items[i]))
- return true;
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i]))
+ return true;
+ } while ((i = inc(i)) != putIndex);
+ }
return false;
} finally {
lock.unlock();
@@ -522,8 +558,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -589,12 +624,20 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- items[i] = null;
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
+ int k = count;
+ if (k > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ items[i] = null;
+ } while ((i = inc(i)) != putIndex);
+ takeIndex = putIndex;
+ count = 0;
+ if (itrs != null)
+ itrs.queueIsEmpty();
+ for (; k > 0 && lock.hasWaiters(notFull); k--)
+ notFull.signal();
+ }
} finally {
lock.unlock();
}
@@ -607,32 +650,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- checkNotNull(c);
- if (c == this)
- throw new IllegalArgumentException();
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int n = 0;
- int max = count;
- while (n < max) {
- c.add(this.<E>cast(items[i]));
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
- }
- return n;
- } finally {
- lock.unlock();
- }
+ return drainTo(c, Integer.MAX_VALUE);
}
/**
@@ -651,21 +669,33 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int i = takeIndex;
- int n = 0;
- int max = (maxElements < count) ? maxElements : count;
- while (n < max) {
- c.add(this.<E>cast(items[i]));
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count -= n;
- takeIndex = i;
- notFull.signalAll();
+ int n = Math.min(maxElements, count);
+ int take = takeIndex;
+ int i = 0;
+ try {
+ while (i < n) {
+ @SuppressWarnings("unchecked") E x = (E) items[take];
+ c.add(x);
+ items[take] = null;
+ take = inc(take);
+ i++;
+ }
+ return n;
+ } finally {
+ // Restore invariants even if c.add() threw
+ if (i > 0) {
+ count -= i;
+ takeIndex = take;
+ if (itrs != null) {
+ if (count == 0)
+ itrs.queueIsEmpty();
+ else if (i > take)
+ itrs.takeIndexWrapped();
+ }
+ for (; i > 0 && lock.hasWaiters(notFull); i--)
+ notFull.signal();
+ }
}
- return n;
} finally {
lock.unlock();
}
@@ -675,12 +705,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned {@code Iterator} is a "weakly consistent" iterator that
+ * <p>The returned iterator is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
- * 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.
+ * 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.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -689,88 +719,627 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Iterator for ArrayBlockingQueue. To maintain weak consistency
- * with respect to puts and takes, we (1) read ahead one slot, so
- * as to not report hasNext true but then not have an element to
- * return -- however we later recheck this slot to use the most
- * current value; (2) ensure that each array slot is traversed at
- * most once (by tracking "remaining" elements); (3) skip over
- * null slots, which can occur if takes race ahead of iterators.
- * However, for circular array-based queues, we cannot rely on any
- * well established definition of what it means to be weakly
- * consistent with respect to interior removes since these may
- * require slot overwrites in the process of sliding elements to
- * cover gaps. So we settle for resiliency, operating on
- * established apparent nexts, which may miss some elements that
- * have moved between calls to next.
+ * Shared data between iterators and their queue, allowing queue
+ * modifications to update iterators when elements are removed.
+ *
+ * This adds a lot of complexity for the sake of correctly
+ * handling some uncommon operations, but the combination of
+ * circular-arrays and supporting interior removes (i.e., those
+ * not at head) would cause iterators to sometimes lose their
+ * places and/or (re)report elements they shouldn't. To avoid
+ * this, when a queue has one or more iterators, it keeps iterator
+ * state consistent by:
+ *
+ * (1) keeping track of the number of "cycles", that is, the
+ * number of times takeIndex has wrapped around to 0.
+ * (2) notifying all iterators via the callback removedAt whenever
+ * an interior element is removed (and thus other elements may
+ * be shifted).
+ *
+ * These suffice to eliminate iterator inconsistencies, but
+ * unfortunately add the secondary responsibility of maintaining
+ * the list of iterators. We track all active iterators in a
+ * simple linked list (accessed only when the queue's lock is
+ * held) of weak references to Itr. The list is cleaned up using
+ * 3 different mechanisms:
+ *
+ * (1) Whenever a new iterator is created, do some O(1) checking for
+ * stale list elements.
+ *
+ * (2) Whenever takeIndex wraps around to 0, check for iterators
+ * that have been unused for more than one wrap-around cycle.
+ *
+ * (3) Whenever the queue becomes empty, all iterators are notified
+ * and this entire data structure is discarded.
+ *
+ * So in addition to the removedAt callback that is necessary for
+ * correctness, iterators have the shutdown and takeIndexWrapped
+ * callbacks that help remove stale iterators from the list.
+ *
+ * Whenever a list element is examined, it is expunged if either
+ * the GC has determined that the iterator is discarded, or if the
+ * iterator reports that it is "detached" (does not need any
+ * further state updates). Overhead is maximal when takeIndex
+ * never advances, iterators are discarded before they are
+ * exhausted, and all removals are interior removes, in which case
+ * all stale iterators are discovered by the GC. But even in this
+ * case we don't increase the amortized complexity.
+ *
+ * Care must be taken to keep list sweeping methods from
+ * reentrantly invoking another such method, causing subtle
+ * corruption bugs.
+ */
+ class Itrs {
+
+ /**
+ * Node in a linked list of weak iterator references.
+ */
+ private class Node extends WeakReference<Itr> {
+ Node next;
+
+ Node(Itr iterator, Node next) {
+ super(iterator);
+ this.next = next;
+ }
+ }
+
+ /** Incremented whenever takeIndex wraps around to 0 */
+ int cycles = 0;
+
+ /** Linked list of weak iterator references */
+ private Node head;
+
+ /** Used to expunge stale iterators */
+ private Node sweeper = null;
+
+ private static final int SHORT_SWEEP_PROBES = 4;
+ private static final int LONG_SWEEP_PROBES = 16;
+
+ Itrs(Itr initial) {
+ register(initial);
+ }
+
+ /**
+ * Sweeps itrs, looking for and expunging stale iterators.
+ * If at least one was found, tries harder to find more.
+ * Called only from iterating thread.
+ *
+ * @param tryHarder whether to start in try-harder mode, because
+ * there is known to be at least one iterator to collect
+ */
+ void doSomeSweeping(boolean tryHarder) {
+ // assert lock.getHoldCount() == 1;
+ // assert head != null;
+ int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
+ Node o, p;
+ final Node sweeper = this.sweeper;
+ boolean passedGo; // to limit search to one full sweep
+
+ if (sweeper == null) {
+ o = null;
+ p = head;
+ passedGo = true;
+ } else {
+ o = sweeper;
+ p = o.next;
+ passedGo = false;
+ }
+
+ for (; probes > 0; probes--) {
+ if (p == null) {
+ if (passedGo)
+ break;
+ o = null;
+ p = head;
+ passedGo = true;
+ }
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.isDetached()) {
+ // found a discarded/exhausted iterator
+ probes = LONG_SWEEP_PROBES; // "try harder"
+ // unlink p
+ p.clear();
+ p.next = null;
+ if (o == null) {
+ head = next;
+ if (next == null) {
+ // We've run out of iterators to track; retire
+ itrs = null;
+ return;
+ }
+ }
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+
+ this.sweeper = (p == null) ? null : o;
+ }
+
+ /**
+ * Adds a new iterator to the linked list of tracked iterators.
+ */
+ void register(Itr itr) {
+ // assert lock.getHoldCount() == 1;
+ head = new Node(itr, head);
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to 0.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ cycles++;
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.takeIndexWrapped()) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void removedAt(int removedIndex) {
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.removedAt(removedIndex)) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever the queue becomes empty.
+ *
+ * Notifies all active iterators that the queue is empty,
+ * clears all weak refs, and unlinks the itrs datastructure.
+ */
+ void queueIsEmpty() {
+ // assert lock.getHoldCount() == 1;
+ for (Node p = head; p != null; p = p.next) {
+ Itr it = p.get();
+ if (it != null) {
+ p.clear();
+ it.shutdown();
+ }
+ }
+ head = null;
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an element has been dequeued (at takeIndex).
+ */
+ void elementDequeued() {
+ // assert lock.getHoldCount() == 1;
+ if (count == 0)
+ queueIsEmpty();
+ else if (takeIndex == 0)
+ takeIndexWrapped();
+ }
+ }
+
+ /**
+ * Iterator for ArrayBlockingQueue.
+ *
+ * To maintain weak consistency with respect to puts and takes, we
+ * read ahead one slot, so as to not report hasNext true but then
+ * not have an element to return.
+ *
+ * We switch into "detached" mode (allowing prompt unlinking from
+ * itrs without help from the GC) when all indices are negative, or
+ * when hasNext returns false for the first time. This allows the
+ * iterator to track concurrent updates completely accurately,
+ * except for the corner case of the user calling Iterator.remove()
+ * after hasNext() returned false. Even in this case, we ensure
+ * that we don't remove the wrong element by keeping track of the
+ * expected element to remove, in lastItem. Yes, we may fail to
+ * remove lastItem from the queue if it moved due to an interleaved
+ * interior remove while in detached mode.
*/
private class Itr implements Iterator<E> {
- private int remaining; // Number of elements yet to be returned
- private int nextIndex; // Index of element to be returned by next
- private E nextItem; // Element to be returned by next call to next
- private E lastItem; // Element returned by last call to next
- private int lastRet; // Index of last element returned, or -1 if none
+ /** Index to look for new nextItem; NONE at end */
+ private int cursor;
+
+ /** Element to be returned by next call to next(); null if none */
+ private E nextItem;
+
+ /** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
+ private int nextIndex;
+
+ /** Last element returned; null if none or not detached. */
+ private E lastItem;
+
+ /** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
+ private int lastRet;
+
+ /** Previous value of takeIndex, or DETACHED when detached */
+ private int prevTakeIndex;
+
+ /** Previous value of iters.cycles */
+ private int prevCycles;
+
+ /** Special index value indicating "not available" or "undefined" */
+ private static final int NONE = -1;
+
+ /**
+ * Special index value indicating "removed elsewhere", that is,
+ * removed by some operation other than a call to this.remove().
+ */
+ private static final int REMOVED = -2;
+
+ /** Special value for prevTakeIndex indicating "detached mode" */
+ private static final int DETACHED = -3;
Itr() {
+ // assert lock.getHoldCount() == 0;
+ lastRet = NONE;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- lastRet = -1;
- if ((remaining = count) > 0)
+ if (count == 0) {
+ // assert itrs == null;
+ cursor = NONE;
+ nextIndex = NONE;
+ prevTakeIndex = DETACHED;
+ } else {
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ prevTakeIndex = takeIndex;
nextItem = itemAt(nextIndex = takeIndex);
+ cursor = incCursor(takeIndex);
+ if (itrs == null) {
+ itrs = new Itrs(this);
+ } else {
+ itrs.register(this); // in this order
+ itrs.doSomeSweeping(false);
+ }
+ prevCycles = itrs.cycles;
+ // assert takeIndex >= 0;
+ // assert prevTakeIndex == takeIndex;
+ // assert nextIndex >= 0;
+ // assert nextItem != null;
+ }
} finally {
lock.unlock();
}
}
+ boolean isDetached() {
+ // assert lock.getHoldCount() == 1;
+ return prevTakeIndex < 0;
+ }
+
+ private int incCursor(int index) {
+ // assert lock.getHoldCount() == 1;
+ index = inc(index);
+ if (index == putIndex)
+ index = NONE;
+ return index;
+ }
+
+ /**
+ * Returns true if index is invalidated by the given number of
+ * dequeues, starting from prevTakeIndex.
+ */
+ private boolean invalidated(int index, int prevTakeIndex,
+ long dequeues, int length) {
+ if (index < 0)
+ return false;
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return dequeues > distance;
+ }
+
+ /**
+ * Adjusts indices to incorporate all dequeues since the last
+ * operation on this iterator. Call only from iterating thread.
+ */
+ private void incorporateDequeues() {
+ // assert lock.getHoldCount() == 1;
+ // assert itrs != null;
+ // assert !isDetached();
+ // assert count > 0;
+
+ final int cycles = itrs.cycles;
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ final int prevCycles = this.prevCycles;
+ final int prevTakeIndex = this.prevTakeIndex;
+
+ if (cycles != prevCycles || takeIndex != prevTakeIndex) {
+ final int len = items.length;
+ // how far takeIndex has advanced since the previous
+ // operation of this iterator
+ long dequeues = (cycles - prevCycles) * len
+ + (takeIndex - prevTakeIndex);
+
+ // Check indices for invalidation
+ if (invalidated(lastRet, prevTakeIndex, dequeues, len))
+ lastRet = REMOVED;
+ if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
+ nextIndex = REMOVED;
+ if (invalidated(cursor, prevTakeIndex, dequeues, len))
+ cursor = takeIndex;
+
+ if (cursor < 0 && nextIndex < 0 && lastRet < 0)
+ detach();
+ else {
+ this.prevCycles = cycles;
+ this.prevTakeIndex = takeIndex;
+ }
+ }
+ }
+
+ /**
+ * Called when itrs should stop tracking this iterator, either
+ * because there are no more indices to update (cursor < 0 &&
+ * nextIndex < 0 && lastRet < 0) or as a special exception, when
+ * lastRet >= 0, because hasNext() is about to return false for the
+ * first time. Call only from iterating thread.
+ */
+ private void detach() {
+ // Switch to detached mode
+ // assert lock.getHoldCount() == 1;
+ // assert cursor == NONE;
+ // assert nextIndex < 0;
+ // assert lastRet < 0 || nextItem == null;
+ // assert lastRet < 0 ^ lastItem != null;
+ if (prevTakeIndex >= 0) {
+ // assert itrs != null;
+ prevTakeIndex = DETACHED;
+ // try to unlink from itrs (but not too hard)
+ itrs.doSomeSweeping(true);
+ }
+ }
+
+ /**
+ * For performance reasons, we would like not to acquire a lock in
+ * hasNext in the common case. To allow for this, we only access
+ * fields (i.e. nextItem) that are not modified by update operations
+ * triggered by queue modifications.
+ */
public boolean hasNext() {
- return remaining > 0;
+ // assert lock.getHoldCount() == 0;
+ if (nextItem != null)
+ return true;
+ noNext();
+ return false;
+ }
+
+ private void noNext() {
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ // assert cursor == NONE;
+ // assert nextIndex == NONE;
+ if (!isDetached()) {
+ // assert lastRet >= 0;
+ incorporateDequeues(); // might update lastRet
+ if (lastRet >= 0) {
+ lastItem = itemAt(lastRet);
+ // assert lastItem != null;
+ detach();
+ }
+ }
+ // assert isDetached();
+ // assert lastRet < 0 ^ lastItem != null;
+ } finally {
+ lock.unlock();
+ }
}
public E next() {
+ // assert lock.getHoldCount() == 0;
+ final E x = nextItem;
+ if (x == null)
+ throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- if (remaining <= 0)
- throw new NoSuchElementException();
+ if (!isDetached())
+ incorporateDequeues();
+ // assert nextIndex != NONE;
+ // assert lastItem == null;
lastRet = nextIndex;
- E x = itemAt(nextIndex); // check for fresher value
- if (x == null) {
- x = nextItem; // we are forced to report old value
- lastItem = null; // but ensure remove fails
+ final int cursor = this.cursor;
+ if (cursor >= 0) {
+ nextItem = itemAt(nextIndex = cursor);
+ // assert nextItem != null;
+ this.cursor = incCursor(cursor);
+ } else {
+ nextIndex = NONE;
+ nextItem = null;
}
- else
- lastItem = x;
- while (--remaining > 0 && // skip over nulls
- (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
- ;
- return x;
} finally {
lock.unlock();
}
+ return x;
}
public void remove() {
+ // assert lock.getHoldCount() == 0;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- int i = lastRet;
- if (i == -1)
+ if (!isDetached())
+ incorporateDequeues(); // might update lastRet or detach
+ final int lastRet = this.lastRet;
+ this.lastRet = NONE;
+ if (lastRet >= 0) {
+ if (!isDetached())
+ removeAt(lastRet);
+ else {
+ final E lastItem = this.lastItem;
+ // assert lastItem != null;
+ this.lastItem = null;
+ if (itemAt(lastRet) == lastItem)
+ removeAt(lastRet);
+ }
+ } else if (lastRet == NONE)
throw new IllegalStateException();
- lastRet = -1;
- E x = lastItem;
- lastItem = null;
- // only remove if item still at index
- if (x != null && x == items[i]) {
- boolean removingHead = (i == takeIndex);
- removeAt(i);
- if (!removingHead)
- nextIndex = dec(nextIndex);
- }
+ // else lastRet == REMOVED and the last returned element was
+ // previously asynchronously removed via an operation other
+ // than this.remove(), so nothing to do.
+
+ if (cursor < 0 && nextIndex < 0)
+ detach();
} finally {
lock.unlock();
+ // assert lastRet == NONE;
+ // assert lastItem == null;
}
}
- }
+ /**
+ * Called to notify the iterator that the queue is empty, or that it
+ * has fallen hopelessly behind, so that it should abandon any
+ * further iteration, except possibly to return one more element
+ * from next(), as promised by returning true from hasNext().
+ */
+ void shutdown() {
+ // assert lock.getHoldCount() == 1;
+ cursor = NONE;
+ if (nextIndex >= 0)
+ nextIndex = REMOVED;
+ if (lastRet >= 0) {
+ lastRet = REMOVED;
+ lastItem = null;
+ }
+ prevTakeIndex = DETACHED;
+ // Don't set nextItem to null because we must continue to be
+ // able to return it on next().
+ //
+ // Caller will unlink from itrs when convenient.
+ }
+
+ private int distance(int index, int prevTakeIndex, int length) {
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return distance;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean removedAt(int removedIndex) {
+ // assert lock.getHoldCount() == 1;
+ if (isDetached())
+ return true;
+
+ final int cycles = itrs.cycles;
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ final int prevCycles = this.prevCycles;
+ final int prevTakeIndex = this.prevTakeIndex;
+ final int len = items.length;
+ int cycleDiff = cycles - prevCycles;
+ if (removedIndex < takeIndex)
+ cycleDiff++;
+ final int removedDistance =
+ (cycleDiff * len) + (removedIndex - prevTakeIndex);
+ // assert removedDistance >= 0;
+ int cursor = this.cursor;
+ if (cursor >= 0) {
+ int x = distance(cursor, prevTakeIndex, len);
+ if (x == removedDistance) {
+ if (cursor == putIndex)
+ this.cursor = cursor = NONE;
+ }
+ else if (x > removedDistance) {
+ // assert cursor != prevTakeIndex;
+ this.cursor = cursor = dec(cursor);
+ }
+ }
+ int lastRet = this.lastRet;
+ if (lastRet >= 0) {
+ int x = distance(lastRet, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.lastRet = lastRet = REMOVED;
+ else if (x > removedDistance)
+ this.lastRet = lastRet = dec(lastRet);
+ }
+ int nextIndex = this.nextIndex;
+ if (nextIndex >= 0) {
+ int x = distance(nextIndex, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.nextIndex = nextIndex = REMOVED;
+ else if (x > removedDistance)
+ this.nextIndex = nextIndex = dec(nextIndex);
+ }
+ else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
+ this.prevTakeIndex = DETACHED;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to zero.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ if (isDetached())
+ return true;
+ if (itrs.cycles - prevCycles > 1) {
+ // All the elements that existed at the time of the last
+ // operation are gone, so abandon further iteration.
+ shutdown();
+ return true;
+ }
+ return false;
+ }
+
+// /** Uncomment for debugging. */
+// public String toString() {
+// return ("cursor=" + cursor + " " +
+// "nextIndex=" + nextIndex + " " +
+// "lastRet=" + lastRet + " " +
+// "nextItem=" + nextItem + " " +
+// "lastItem=" + lastItem + " " +
+// "prevCycles=" + prevCycles + " " +
+// "prevTakeIndex=" + prevTakeIndex + " " +
+// "size()=" + size() + " " +
+// "remainingCapacity()=" + remainingCapacity());
+// }
+ }
}
diff --git a/luni/src/main/java/java/util/concurrent/BlockingDeque.java b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
index 136df9c..34f103d 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -36,9 +36,9 @@ import java.util.*;
* <tr>
* <td><b>Insert</b></td>
* <td>{@link #addFirst addFirst(e)}</td>
- * <td>{@link #offerFirst offerFirst(e)}</td>
+ * <td>{@link #offerFirst(Object) offerFirst(e)}</td>
* <td>{@link #putFirst putFirst(e)}</td>
- * <td>{@link #offerFirst offerFirst(e, time, unit)}</td>
+ * <td>{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -67,9 +67,9 @@ import java.util.*;
* <tr>
* <td><b>Insert</b></td>
* <td>{@link #addLast addLast(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
* <td>{@link #putLast putLast(e)}</td>
- * <td>{@link #offerLast offerLast(e, time, unit)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -106,20 +106,20 @@ import java.util.*;
* <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td>
* </tr>
* <tr>
- * <td>{@link #add add(e)}</td>
- * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link #add(Object) add(e)}</td>
+ * <td>{@link #addLast(Object) addLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #offer offer(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td>{@link #offer(Object) offer(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #put put(e)}</td>
- * <td>{@link #putLast putLast(e)}</td>
+ * <td>{@link #put(Object) put(e)}</td>
+ * <td>{@link #putLast(Object) putLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #offer offer(e, time, unit)}</td>
- * <td>{@link #offerLast offerLast(e, time, unit)}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
* <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td>
@@ -181,7 +181,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* possible to do so immediately without violating capacity restrictions,
* throwing an <tt>IllegalStateException</tt> if no space is currently
* available. When using a capacity-restricted deque, it is generally
- * preferable to use {@link #offerFirst offerFirst}.
+ * preferable to use {@link #offerFirst(Object) offerFirst}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -196,7 +196,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* possible to do so immediately without violating capacity restrictions,
* throwing an <tt>IllegalStateException</tt> if no space is currently
* available. When using a capacity-restricted deque, it is generally
- * preferable to use {@link #offerLast offerLast}.
+ * preferable to use {@link #offerLast(Object) offerLast}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -212,7 +212,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* returning <tt>true</tt> upon success and <tt>false</tt> if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
- * preferable to the {@link #addFirst addFirst} method, which can
+ * preferable to the {@link #addFirst(Object) addFirst} method, which can
* fail to insert an element only by throwing an exception.
*
* @param e the element to add
@@ -228,7 +228,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* returning <tt>true</tt> upon success and <tt>false</tt> if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
- * preferable to the {@link #addLast addLast} method, which can
+ * preferable to the {@link #addLast(Object) addLast} method, which can
* fail to insert an element only by throwing an exception.
*
* @param e the element to add
@@ -371,8 +371,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
@@ -387,8 +389,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
@@ -401,9 +405,9 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
- * <p>This method is equivalent to {@link #addLast addLast}.
+ * <p>This method is equivalent to {@link #addLast(Object) addLast}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -424,7 +428,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
*
- * <p>This method is equivalent to {@link #offerLast offerLast}.
+ * <p>This method is equivalent to {@link #offerLast(Object) offerLast}.
*
* @param e the element to add
* @throws ClassCastException if the class of the specified element
@@ -440,7 +444,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* (in other words, at the tail of this deque), waiting if necessary for
* space to become available.
*
- * <p>This method is equivalent to {@link #putLast putLast}.
+ * <p>This method is equivalent to {@link #putLast(Object) putLast}.
*
* @param e the element to add
* @throws InterruptedException {@inheritDoc}
@@ -458,7 +462,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* specified wait time if necessary for space to become available.
*
* <p>This method is equivalent to
- * {@link #offerLast offerLast}.
+ * {@link #offerLast(Object,long,TimeUnit) offerLast}.
*
* @param e the element to add
* @return <tt>true</tt> if the element was added to this deque, else
@@ -557,13 +561,15 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* (or equivalently, if this deque changed as a result of the call).
*
* <p>This method is equivalent to
- * {@link #removeFirstOccurrence removeFirstOccurrence}.
+ * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if this deque changed as a result of the call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -575,8 +581,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o object to be checked for containment in this deque
* @return <tt>true</tt> if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
@@ -602,7 +610,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* words, inserts the element at the front of this deque unless it would
* violate capacity restrictions.
*
- * <p>This method is equivalent to {@link #addFirst addFirst}.
+ * <p>This method is equivalent to {@link #addFirst(Object) addFirst}.
*
* @throws IllegalStateException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
diff --git a/luni/src/main/java/java/util/concurrent/BlockingQueue.java b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
index d01c097..6cfe52b 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -42,7 +42,7 @@ import java.util.Queue;
* <td>{@link #add add(e)}</td>
* <td>{@link #offer offer(e)}</td>
* <td>{@link #put put(e)}</td>
- * <td>{@link #offer offer(e, time, unit)}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -102,7 +102,7 @@ import java.util.Queue;
* Usage example, based on a typical producer-consumer scenario.
* Note that a <tt>BlockingQueue</tt> can safely be used with multiple
* producers and multiple consumers.
- * <pre>
+ * <pre> {@code
* class Producer implements Runnable {
* private final BlockingQueue queue;
* Producer(BlockingQueue q) { queue = q; }
@@ -135,8 +135,7 @@ import java.util.Queue;
* new Thread(c1).start();
* new Thread(c2).start();
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
@@ -156,7 +155,7 @@ public interface BlockingQueue<E> extends Queue<E> {
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted queue, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
* @param e the element to add
* @return <tt>true</tt> (as specified by {@link Collection#add})
@@ -274,8 +273,10 @@ public interface BlockingQueue<E> extends Queue<E> {
* @param o element to be removed from this queue, if present
* @return <tt>true</tt> if this queue changed as a result of the call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this queue (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this queue
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -287,8 +288,10 @@ public interface BlockingQueue<E> extends Queue<E> {
* @param o object to be checked for containment in this queue
* @return <tt>true</tt> if this queue contains the specified element
* @throws ClassCastException if the class of the specified element
- * is incompatible with this queue (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this queue
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
diff --git a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
index 3f93fbb..76fae13 100644
--- a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
+++ b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/Callable.java b/luni/src/main/java/java/util/concurrent/Callable.java
index abc4d04..293544b 100644
--- a/luni/src/main/java/java/util/concurrent/Callable.java
+++ b/luni/src/main/java/java/util/concurrent/Callable.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/CancellationException.java b/luni/src/main/java/java/util/concurrent/CancellationException.java
index 2c29544..dc452e4 100644
--- a/luni/src/main/java/java/util/concurrent/CancellationException.java
+++ b/luni/src/main/java/java/util/concurrent/CancellationException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/CompletionService.java b/luni/src/main/java/java/util/concurrent/CompletionService.java
index df9f719..7b0931c 100644
--- a/luni/src/main/java/java/util/concurrent/CompletionService.java
+++ b/luni/src/main/java/java/util/concurrent/CompletionService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index c142e2a..c85a5cc 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -1,16 +1,13 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
import java.util.*;
import java.io.Serializable;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
// BEGIN android-note
// removed link to collections framework docs
@@ -76,7 +73,25 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
/*
* The basic strategy is to subdivide the table among Segments,
- * each of which itself is a concurrently readable hash table.
+ * each of which itself is a concurrently readable hash table. To
+ * reduce footprint, all but one segments are constructed only
+ * when first needed (see ensureSegment). To maintain visibility
+ * in the presence of lazy construction, accesses to segments as
+ * well as elements of segment's table must use volatile access,
+ * which is done via Unsafe within methods segmentAt etc
+ * below. These provide the functionality of AtomicReferenceArrays
+ * but reduce the levels of indirection. Additionally,
+ * volatile-writes of table elements and entry "next" fields
+ * within locked operations use the cheaper "lazySet" forms of
+ * writes (via putOrderedObject) because these writes are always
+ * followed by lock releases that maintain sequential consistency
+ * of table updates.
+ *
+ * Historical note: The previous version of this class relied
+ * heavily on "final" fields, which avoided some volatile reads at
+ * the expense of a large initial footprint. Some remnants of
+ * that design (including forced construction of segment 0) exist
+ * to ensure serialization compatibility.
*/
/* ---------------- Constants -------------- */
@@ -108,8 +123,15 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
+ * The minimum capacity for per-segment tables. Must be a power
+ * of two, at least two to avoid immediate resizing on next use
+ * after lazy construction.
+ */
+ static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
+
+ /**
* The maximum number of segments to allow; used to bound
- * constructor arguments.
+ * constructor arguments. Must be power of two less than 1 << 24.
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
@@ -135,7 +157,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
final int segmentShift;
/**
- * The segments, each of which is a specialized hash table
+ * The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments;
@@ -143,7 +165,66 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
transient Set<Map.Entry<K,V>> entrySet;
transient Collection<V> values;
- /* ---------------- Small Utilities -------------- */
+ /**
+ * ConcurrentHashMap list entry. Note that this is never exported
+ * out as a user-visible Map.Entry.
+ */
+ static final class HashEntry<K,V> {
+ final int hash;
+ final K key;
+ volatile V value;
+ volatile HashEntry<K,V> next;
+
+ HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ /**
+ * Sets next field with volatile write semantics. (See above
+ * about use of putOrderedObject.)
+ */
+ final void setNext(HashEntry<K,V> n) {
+ UNSAFE.putOrderedObject(this, nextOffset, n);
+ }
+
+ // Unsafe mechanics
+ static final sun.misc.Unsafe UNSAFE;
+ static final long nextOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = HashEntry.class;
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /**
+ * Gets the ith element of given table (if nonnull) with volatile
+ * read semantics. Note: This is manually integrated into a few
+ * performance-sensitive methods to reduce call overhead.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) {
+ return (tab == null) ? null :
+ (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)i << TSHIFT) + TBASE);
+ }
+
+ /**
+ * Sets the ith element of given table, with volatile write
+ * semantics. (See above about use of putOrderedObject.)
+ */
+ static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i,
+ HashEntry<K,V> e) {
+ UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e);
+ }
/**
* Applies a supplemental hash function to a given hashCode, which
@@ -164,104 +245,67 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
/**
- * Returns the segment that should be used for key with given hash
- * @param hash the hash code for the key
- * @return the segment
- */
- final Segment<K,V> segmentFor(int hash) {
- return segments[(hash >>> segmentShift) & segmentMask];
- }
-
- /* ---------------- Inner Classes -------------- */
-
- /**
- * ConcurrentHashMap list entry. Note that this is never exported
- * out as a user-visible Map.Entry.
- *
- * Because the value field is volatile, not final, it is legal wrt
- * the Java Memory Model for an unsynchronized reader to see null
- * instead of initial value when read via a data race. Although a
- * reordering leading to this is not likely to ever actually
- * occur, the Segment.readValueUnderLock method is used as a
- * backup in case a null (pre-initialized) value is ever seen in
- * an unsynchronized access method.
- */
- static final class HashEntry<K,V> {
- final K key;
- final int hash;
- volatile V value;
- final HashEntry<K,V> next;
-
- HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
- this.key = key;
- this.hash = hash;
- this.next = next;
- this.value = value;
- }
-
- @SuppressWarnings("unchecked")
- static final <K,V> HashEntry<K,V>[] newArray(int i) {
- return new HashEntry[i];
- }
- }
-
- /**
* Segments are specialized versions of hash tables. This
* subclasses from ReentrantLock opportunistically, just to
* simplify some locking and avoid separate construction.
*/
static final class Segment<K,V> extends ReentrantLock implements Serializable {
/*
- * Segments maintain a table of entry lists that are ALWAYS
- * kept in a consistent state, so can be read without locking.
- * Next fields of nodes are immutable (final). All list
- * additions are performed at the front of each bin. This
- * makes it easy to check changes, and also fast to traverse.
- * When nodes would otherwise be changed, new nodes are
- * created to replace them. This works well for hash tables
- * since the bin lists tend to be short. (The average length
- * is less than two for the default load factor threshold.)
- *
- * Read operations can thus proceed without locking, but rely
- * on selected uses of volatiles to ensure that completed
- * write operations performed by other threads are
- * noticed. For most purposes, the "count" field, tracking the
- * number of elements, serves as that volatile variable
- * ensuring visibility. This is convenient because this field
- * needs to be read in many read operations anyway:
- *
- * - All (unsynchronized) read operations must first read the
- * "count" field, and should not look at table entries if
- * it is 0.
+ * Segments maintain a table of entry lists that are always
+ * kept in a consistent state, so can be read (via volatile
+ * reads of segments and tables) without locking. This
+ * requires replicating nodes when necessary during table
+ * resizing, so the old lists can be traversed by readers
+ * still using old version of table.
*
- * - All (synchronized) write operations should write to
- * the "count" field after structurally changing any bin.
- * The operations must not take any action that could even
- * momentarily cause a concurrent read operation to see
- * inconsistent data. This is made easier by the nature of
- * the read operations in Map. For example, no operation
- * can reveal that the table has grown but the threshold
- * has not yet been updated, so there are no atomicity
- * requirements for this with respect to reads.
- *
- * As a guide, all critical volatile reads and writes to the
- * count field are marked in code comments.
+ * This class defines only mutative methods requiring locking.
+ * Except as noted, the methods of this class perform the
+ * per-segment versions of ConcurrentHashMap methods. (Other
+ * methods are integrated directly into ConcurrentHashMap
+ * methods.) These mutative methods use a form of controlled
+ * spinning on contention via methods scanAndLock and
+ * scanAndLockForPut. These intersperse tryLocks with
+ * traversals to locate nodes. The main benefit is to absorb
+ * cache misses (which are very common for hash tables) while
+ * obtaining locks so that traversal is faster once
+ * acquired. We do not actually use the found nodes since they
+ * must be re-acquired under lock anyway to ensure sequential
+ * consistency of updates (and in any case may be undetectably
+ * stale), but they will normally be much faster to re-locate.
+ * Also, scanAndLockForPut speculatively creates a fresh node
+ * to use in put if no node is found.
*/
private static final long serialVersionUID = 2249069246763182397L;
/**
- * The number of elements in this segment's region.
+ * The maximum number of times to tryLock in a prescan before
+ * possibly blocking on acquire in preparation for a locked
+ * segment operation. On multiprocessors, using a bounded
+ * number of retries maintains cache acquired while locating
+ * nodes.
*/
- transient volatile int count;
+ static final int MAX_SCAN_RETRIES =
+ Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
+
+ /**
+ * The per-segment table. Elements are accessed via
+ * entryAt/setEntryAt providing volatile semantics.
+ */
+ transient volatile HashEntry<K,V>[] table;
/**
- * Number of updates that alter the size of the table. This is
- * used during bulk-read methods to make sure they see a
- * consistent snapshot: If modCounts change during a traversal
- * of segments computing size or checking containsValue, then
- * we might have an inconsistent view of state so (usually)
- * must retry.
+ * The number of elements. Accessed only either within locks
+ * or among other volatile reads that maintain visibility.
+ */
+ transient int count;
+
+ /**
+ * The total number of mutative operations in this segment.
+ * Even though this may overflows 32 bits, it provides
+ * sufficient accuracy for stability checks in CHM isEmpty()
+ * and size() methods. Accessed only either within locks or
+ * among other volatile reads that maintain visibility.
*/
transient int modCount;
@@ -273,11 +317,6 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
transient int threshold;
/**
- * The per-segment table.
- */
- transient volatile HashEntry<K,V>[] table;
-
- /**
* The load factor for the hash table. Even though this value
* is same for all segments, it is replicated to avoid needing
* links to outer object.
@@ -285,202 +324,93 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
*/
final float loadFactor;
- Segment(int initialCapacity, float lf) {
- loadFactor = lf;
- setTable(HashEntry.<K,V>newArray(initialCapacity));
- }
-
- @SuppressWarnings("unchecked")
- static final <K,V> Segment<K,V>[] newArray(int i) {
- return new Segment[i];
- }
-
- /**
- * Sets table to new HashEntry array.
- * Call only while holding lock or in constructor.
- */
- void setTable(HashEntry<K,V>[] newTable) {
- threshold = (int)(newTable.length * loadFactor);
- table = newTable;
- }
-
- /**
- * Returns properly casted first entry of bin for given hash.
- */
- HashEntry<K,V> getFirst(int hash) {
- HashEntry<K,V>[] tab = table;
- return tab[hash & (tab.length - 1)];
+ Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
+ this.loadFactor = lf;
+ this.threshold = threshold;
+ this.table = tab;
}
- /**
- * Reads value field of an entry under lock. Called if value
- * field ever appears to be null. This is possible only if a
- * compiler happens to reorder a HashEntry initialization with
- * its table assignment, which is legal under memory model
- * but is not known to ever occur.
- */
- V readValueUnderLock(HashEntry<K,V> e) {
- lock();
+ final V put(K key, int hash, V value, boolean onlyIfAbsent) {
+ HashEntry<K,V> node = tryLock() ? null :
+ scanAndLockForPut(key, hash, value);
+ V oldValue;
try {
- return e.value;
- } finally {
- unlock();
- }
- }
-
- /* Specialized implementations of map methods */
-
- V get(Object key, int hash) {
- if (count != 0) { // read-volatile
- HashEntry<K,V> e = getFirst(hash);
- while (e != null) {
- if (e.hash == hash && key.equals(e.key)) {
- V v = e.value;
- if (v != null)
- return v;
- return readValueUnderLock(e); // recheck
- }
- e = e.next;
- }
- }
- return null;
- }
-
- boolean containsKey(Object key, int hash) {
- if (count != 0) { // read-volatile
- HashEntry<K,V> e = getFirst(hash);
- while (e != null) {
- if (e.hash == hash && key.equals(e.key))
- return true;
- e = e.next;
- }
- }
- return false;
- }
-
- boolean containsValue(Object value) {
- if (count != 0) { // read-volatile
HashEntry<K,V>[] tab = table;
- int len = tab.length;
- for (int i = 0 ; i < len; i++) {
- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
- V v = e.value;
- if (v == null) // recheck
- v = readValueUnderLock(e);
- if (value.equals(v))
- return true;
+ int index = (tab.length - 1) & hash;
+ HashEntry<K,V> first = entryAt(tab, index);
+ for (HashEntry<K,V> e = first;;) {
+ if (e != null) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ oldValue = e.value;
+ if (!onlyIfAbsent) {
+ e.value = value;
+ ++modCount;
+ }
+ break;
+ }
+ e = e.next;
+ }
+ else {
+ if (node != null)
+ node.setNext(first);
+ else
+ node = new HashEntry<K,V>(hash, key, value, first);
+ int c = count + 1;
+ if (c > threshold && tab.length < MAXIMUM_CAPACITY)
+ rehash(node);
+ else
+ setEntryAt(tab, index, node);
+ ++modCount;
+ count = c;
+ oldValue = null;
+ break;
}
}
- }
- return false;
- }
-
- boolean replace(K key, int hash, V oldValue, V newValue) {
- lock();
- try {
- HashEntry<K,V> e = getFirst(hash);
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- boolean replaced = false;
- if (e != null && oldValue.equals(e.value)) {
- replaced = true;
- e.value = newValue;
- }
- return replaced;
- } finally {
- unlock();
- }
- }
-
- V replace(K key, int hash, V newValue) {
- lock();
- try {
- HashEntry<K,V> e = getFirst(hash);
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- V oldValue = null;
- if (e != null) {
- oldValue = e.value;
- e.value = newValue;
- }
- return oldValue;
- } finally {
- unlock();
- }
- }
-
-
- V put(K key, int hash, V value, boolean onlyIfAbsent) {
- lock();
- try {
- int c = count;
- if (c++ > threshold) // ensure capacity
- rehash();
- HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- V oldValue;
- if (e != null) {
- oldValue = e.value;
- if (!onlyIfAbsent)
- e.value = value;
- }
- else {
- oldValue = null;
- ++modCount;
- tab[index] = new HashEntry<K,V>(key, hash, first, value);
- count = c; // write-volatile
- }
- return oldValue;
} finally {
unlock();
}
+ return oldValue;
}
- void rehash() {
- HashEntry<K,V>[] oldTable = table;
- int oldCapacity = oldTable.length;
- if (oldCapacity >= MAXIMUM_CAPACITY)
- return;
-
+ /**
+ * Doubles size of table and repacks entries, also adding the
+ * given node to new table
+ */
+ @SuppressWarnings("unchecked")
+ private void rehash(HashEntry<K,V> node) {
/*
- * Reclassify nodes in each list to new Map. Because we are
- * using power-of-two expansion, the elements from each bin
- * must either stay at same index, or move with a power of two
- * offset. We eliminate unnecessary node creation by catching
- * cases where old nodes can be reused because their next
- * fields won't change. Statistically, at the default
- * threshold, only about one-sixth of them need cloning when
- * a table doubles. The nodes they replace will be garbage
- * collectable as soon as they are no longer referenced by any
- * reader thread that may be in the midst of traversing table
- * right now.
+ * Reclassify nodes in each list to new table. Because we
+ * are using power-of-two expansion, the elements from
+ * each bin must either stay at same index, or move with a
+ * power of two offset. We eliminate unnecessary node
+ * creation by catching cases where old nodes can be
+ * reused because their next fields won't change.
+ * Statistically, at the default threshold, only about
+ * one-sixth of them need cloning when a table
+ * doubles. The nodes they replace will be garbage
+ * collectable as soon as they are no longer referenced by
+ * any reader thread that may be in the midst of
+ * concurrently traversing table. Entry accesses use plain
+ * array indexing because they are followed by volatile
+ * table write.
*/
-
- HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
- threshold = (int)(newTable.length * loadFactor);
- int sizeMask = newTable.length - 1;
+ HashEntry<K,V>[] oldTable = table;
+ int oldCapacity = oldTable.length;
+ int newCapacity = oldCapacity << 1;
+ threshold = (int)(newCapacity * loadFactor);
+ HashEntry<K,V>[] newTable =
+ (HashEntry<K,V>[]) new HashEntry<?,?>[newCapacity];
+ int sizeMask = newCapacity - 1;
for (int i = 0; i < oldCapacity ; i++) {
- // We need to guarantee that any existing reads of old Map can
- // proceed. So we cannot yet null out each bin.
HashEntry<K,V> e = oldTable[i];
-
if (e != null) {
HashEntry<K,V> next = e.next;
int idx = e.hash & sizeMask;
-
- // Single node on list
- if (next == null)
+ if (next == null) // Single node on list
newTable[idx] = e;
-
- else {
- // Reuse trailing consecutive sequence at same slot
+ else { // Reuse consecutive sequence at same slot
HashEntry<K,V> lastRun = e;
int lastIdx = idx;
for (HashEntry<K,V> last = next;
@@ -493,74 +423,263 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
}
newTable[lastIdx] = lastRun;
-
- // Clone all remaining nodes
+ // Clone remaining nodes
for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
- int k = p.hash & sizeMask;
+ V v = p.value;
+ int h = p.hash;
+ int k = h & sizeMask;
HashEntry<K,V> n = newTable[k];
- newTable[k] = new HashEntry<K,V>(p.key, p.hash,
- n, p.value);
+ newTable[k] = new HashEntry<K,V>(h, p.key, v, n);
}
}
}
}
+ int nodeIndex = node.hash & sizeMask; // add the new node
+ node.setNext(newTable[nodeIndex]);
+ newTable[nodeIndex] = node;
table = newTable;
}
/**
+ * Scans for a node containing given key while trying to
+ * acquire lock, creating and returning one if not found. Upon
+ * return, guarantees that lock is held. Unlike in most
+ * methods, calls to method equals are not screened: Since
+ * traversal speed doesn't matter, we might as well help warm
+ * up the associated code and accesses as well.
+ *
+ * @return a new node if key not found, else null
+ */
+ private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
+ HashEntry<K,V> first = entryForHash(this, hash);
+ HashEntry<K,V> e = first;
+ HashEntry<K,V> node = null;
+ int retries = -1; // negative while locating node
+ while (!tryLock()) {
+ HashEntry<K,V> f; // to recheck first below
+ if (retries < 0) {
+ if (e == null) {
+ if (node == null) // speculatively create node
+ node = new HashEntry<K,V>(hash, key, value, null);
+ retries = 0;
+ }
+ else if (key.equals(e.key))
+ retries = 0;
+ else
+ e = e.next;
+ }
+ else if (++retries > MAX_SCAN_RETRIES) {
+ lock();
+ break;
+ }
+ else if ((retries & 1) == 0 &&
+ (f = entryForHash(this, hash)) != first) {
+ e = first = f; // re-traverse if entry changed
+ retries = -1;
+ }
+ }
+ return node;
+ }
+
+ /**
+ * Scans for a node containing the given key while trying to
+ * acquire lock for a remove or replace operation. Upon
+ * return, guarantees that lock is held. Note that we must
+ * lock even if the key is not found, to ensure sequential
+ * consistency of updates.
+ */
+ private void scanAndLock(Object key, int hash) {
+ // similar to but simpler than scanAndLockForPut
+ HashEntry<K,V> first = entryForHash(this, hash);
+ HashEntry<K,V> e = first;
+ int retries = -1;
+ while (!tryLock()) {
+ HashEntry<K,V> f;
+ if (retries < 0) {
+ if (e == null || key.equals(e.key))
+ retries = 0;
+ else
+ e = e.next;
+ }
+ else if (++retries > MAX_SCAN_RETRIES) {
+ lock();
+ break;
+ }
+ else if ((retries & 1) == 0 &&
+ (f = entryForHash(this, hash)) != first) {
+ e = first = f;
+ retries = -1;
+ }
+ }
+ }
+
+ /**
* Remove; match on key only if value null, else match both.
*/
- V remove(Object key, int hash, Object value) {
- lock();
+ final V remove(Object key, int hash, Object value) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ V oldValue = null;
try {
- int c = count - 1;
HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
+ int index = (tab.length - 1) & hash;
+ HashEntry<K,V> e = entryAt(tab, index);
+ HashEntry<K,V> pred = null;
+ while (e != null) {
+ K k;
+ HashEntry<K,V> next = e.next;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ V v = e.value;
+ if (value == null || value == v || value.equals(v)) {
+ if (pred == null)
+ setEntryAt(tab, index, next);
+ else
+ pred.setNext(next);
+ ++modCount;
+ --count;
+ oldValue = v;
+ }
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ } finally {
+ unlock();
+ }
+ return oldValue;
+ }
- V oldValue = null;
- if (e != null) {
- V v = e.value;
- if (value == null || value.equals(v)) {
- oldValue = v;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
+ final boolean replace(K key, int hash, V oldValue, V newValue) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ boolean replaced = false;
+ try {
+ HashEntry<K,V> e;
+ for (e = entryForHash(this, hash); e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ if (oldValue.equals(e.value)) {
+ e.value = newValue;
+ ++modCount;
+ replaced = true;
+ }
+ break;
+ }
+ }
+ } finally {
+ unlock();
+ }
+ return replaced;
+ }
+
+ final V replace(K key, int hash, V value) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ V oldValue = null;
+ try {
+ HashEntry<K,V> e;
+ for (e = entryForHash(this, hash); e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ oldValue = e.value;
+ e.value = value;
++modCount;
- HashEntry<K,V> newFirst = e.next;
- for (HashEntry<K,V> p = first; p != e; p = p.next)
- newFirst = new HashEntry<K,V>(p.key, p.hash,
- newFirst, p.value);
- tab[index] = newFirst;
- count = c; // write-volatile
+ break;
}
}
- return oldValue;
+ } finally {
+ unlock();
+ }
+ return oldValue;
+ }
+
+ final void clear() {
+ lock();
+ try {
+ HashEntry<K,V>[] tab = table;
+ for (int i = 0; i < tab.length ; i++)
+ setEntryAt(tab, i, null);
+ ++modCount;
+ count = 0;
} finally {
unlock();
}
}
+ }
+
+ // Accessing segments
+
+ /**
+ * Gets the jth element of given segment array (if nonnull) with
+ * volatile element access semantics via Unsafe. (The null check
+ * can trigger harmlessly only during deserialization.) Note:
+ * because each element of segments array is set only once (using
+ * fully ordered writes), some performance-sensitive methods rely
+ * on this method only as a recheck upon null reads.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) {
+ long u = (j << SSHIFT) + SBASE;
+ return ss == null ? null :
+ (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u);
+ }
- void clear() {
- if (count != 0) {
- lock();
- try {
- HashEntry<K,V>[] tab = table;
- for (int i = 0; i < tab.length ; i++)
- tab[i] = null;
- ++modCount;
- count = 0; // write-volatile
- } finally {
- unlock();
+ /**
+ * Returns the segment for the given index, creating it and
+ * recording in segment table (via CAS) if not already present.
+ *
+ * @param k the index
+ * @return the segment
+ */
+ @SuppressWarnings("unchecked")
+ private Segment<K,V> ensureSegment(int k) {
+ final Segment<K,V>[] ss = this.segments;
+ long u = (k << SSHIFT) + SBASE; // raw offset
+ Segment<K,V> seg;
+ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
+ Segment<K,V> proto = ss[0]; // use segment 0 as prototype
+ int cap = proto.table.length;
+ float lf = proto.loadFactor;
+ int threshold = (int)(cap * lf);
+ HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry<?,?>[cap];
+ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
+ == null) { // recheck
+ Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
+ while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
+ == null) {
+ if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
+ break;
}
}
}
+ return seg;
}
+ // Hash-based segment and entry accesses
+ /**
+ * Gets the segment for the given hash code.
+ */
+ @SuppressWarnings("unchecked")
+ private Segment<K,V> segmentForHash(int h) {
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u);
+ }
+
+ /**
+ * Gets the table entry for the given segment and hash code.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) {
+ HashEntry<K,V>[] tab;
+ return (seg == null || (tab = seg.table) == null) ? null :
+ (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ }
/* ---------------- Public operations -------------- */
@@ -580,14 +699,13 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* negative or the load factor or concurrencyLevel are
* nonpositive.
*/
+ @SuppressWarnings("unchecked")
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
-
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
-
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
@@ -595,21 +713,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
++sshift;
ssize <<= 1;
}
- segmentShift = 32 - sshift;
- segmentMask = ssize - 1;
- this.segments = Segment.newArray(ssize);
-
+ this.segmentShift = 32 - sshift;
+ this.segmentMask = ssize - 1;
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
- int cap = 1;
+ int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
-
- for (int i = 0; i < this.segments.length; ++i)
- this.segments[i] = new Segment<K,V>(cap, loadFactor);
+ // create segments and segments[0]
+ Segment<K,V> s0 =
+ new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
+ (HashEntry<K,V>[])new HashEntry<?,?>[cap]);
+ Segment<K,V>[] ss = (Segment<K,V>[])new Segment<?,?>[ssize];
+ UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
+ this.segments = ss;
}
/**
@@ -672,33 +792,36 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @return <tt>true</tt> if this map contains no key-value mappings
*/
public boolean isEmpty() {
- final Segment<K,V>[] segments = this.segments;
/*
- * We keep track of per-segment modCounts to avoid ABA
- * problems in which an element in one segment was added and
- * in another removed during traversal, in which case the
- * table was never actually empty at any point. Note the
- * similar use of modCounts in the size() and containsValue()
- * methods, which are the only other methods also susceptible
- * to ABA problems.
+ * Sum per-segment modCounts to avoid mis-reporting when
+ * elements are concurrently added and removed in one segment
+ * while checking another, in which case the table was never
+ * actually empty at any point. (The sum ensures accuracy up
+ * through at least 1<<31 per-segment modifications before
+ * recheck.) Methods size() and containsValue() use similar
+ * constructions for stability checks.
*/
- int[] mc = new int[segments.length];
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].count != 0)
- return false;
- else
- mcsum += mc[i] = segments[i].modCount;
- }
- // If mcsum happens to be zero, then we know we got a snapshot
- // before any modifications at all were made. This is
- // probably common enough to bother tracking.
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].count != 0 ||
- mc[i] != segments[i].modCount)
+ long sum = 0L;
+ final Segment<K,V>[] segments = this.segments;
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> seg = segmentAt(segments, j);
+ if (seg != null) {
+ if (seg.count != 0)
return false;
+ sum += seg.modCount;
+ }
+ }
+ if (sum != 0L) { // recheck unless no modifications
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> seg = segmentAt(segments, j);
+ if (seg != null) {
+ if (seg.count != 0)
+ return false;
+ sum -= seg.modCount;
+ }
}
+ if (sum != 0L)
+ return false;
}
return true;
}
@@ -711,45 +834,36 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @return the number of key-value mappings in this map
*/
public int size() {
- final Segment<K,V>[] segments = this.segments;
- long sum = 0;
- long check = 0;
- int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
- check = 0;
- sum = 0;
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- sum += segments[i].count;
- mcsum += mc[i] = segments[i].modCount;
- }
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- check += segments[i].count;
- if (mc[i] != segments[i].modCount) {
- check = -1; // force retry
- break;
- }
+ final Segment<K,V>[] segments = this.segments;
+ final int segmentCount = segments.length;
+
+ long previousSum = 0L;
+ for (int retries = -1; retries < RETRIES_BEFORE_LOCK; retries++) {
+ long sum = 0L; // sum of modCounts
+ long size = 0L;
+ for (int i = 0; i < segmentCount; i++) {
+ Segment<K,V> segment = segmentAt(segments, i);
+ if (segment != null) {
+ sum += segment.modCount;
+ size += segment.count;
}
}
- if (check == sum)
- break;
+ if (sum == previousSum)
+ return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
+ previousSum = sum;
}
- if (check != sum) { // Resort to locking all segments
- sum = 0;
- for (int i = 0; i < segments.length; ++i)
- segments[i].lock();
- for (int i = 0; i < segments.length; ++i)
- sum += segments[i].count;
- for (int i = 0; i < segments.length; ++i)
- segments[i].unlock();
+
+ long size = 0L;
+ for (int i = 0; i < segmentCount; i++) {
+ Segment<K,V> segment = ensureSegment(i);
+ segment.lock();
+ size += segment.count;
}
- if (sum > Integer.MAX_VALUE)
- return Integer.MAX_VALUE;
- else
- return (int)sum;
+ for (int i = 0; i < segmentCount; i++)
+ segments[i].unlock();
+ return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
}
/**
@@ -764,8 +878,21 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @throws NullPointerException if the specified key is null
*/
public V get(Object key) {
- int hash = hash(key.hashCode());
- return segmentFor(hash).get(key, hash);
+ Segment<K,V> s; // manually integrate access methods to reduce overhead
+ HashEntry<K,V>[] tab;
+ int h = hash(key.hashCode());
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
+ (tab = s.table) != null) {
+ for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key || (e.hash == h && key.equals(k)))
+ return e.value;
+ }
+ }
+ return null;
}
/**
@@ -777,9 +904,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* <tt>equals</tt> method; <tt>false</tt> otherwise.
* @throws NullPointerException if the specified key is null
*/
+ @SuppressWarnings("unchecked")
public boolean containsKey(Object key) {
- int hash = hash(key.hashCode());
- return segmentFor(hash).containsKey(key, hash);
+ Segment<K,V> s; // same as get() except no need for volatile value read
+ HashEntry<K,V>[] tab;
+ int h = hash(key.hashCode());
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
+ (tab = s.table) != null) {
+ for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key || (e.hash == h && key.equals(k)))
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -794,53 +935,47 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @throws NullPointerException if the specified value is null
*/
public boolean containsValue(Object value) {
+ // Same idea as size()
if (value == null)
throw new NullPointerException();
-
- // See explanation of modCount use above
-
final Segment<K,V>[] segments = this.segments;
- int[] mc = new int[segments.length];
-
- // Try a few times without locking
- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
- int sum = 0;
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- int c = segments[i].count;
- mcsum += mc[i] = segments[i].modCount;
- if (segments[i].containsValue(value))
- return true;
- }
- boolean cleanSweep = true;
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- int c = segments[i].count;
- if (mc[i] != segments[i].modCount) {
- cleanSweep = false;
- break;
- }
- }
- }
- if (cleanSweep)
- return false;
- }
- // Resort to locking all segments
- for (int i = 0; i < segments.length; ++i)
- segments[i].lock();
- boolean found = false;
+ long previousSum = 0L;
+ int lockCount = 0;
try {
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].containsValue(value)) {
- found = true;
- break;
+ for (int retries = -1; ; retries++) {
+ long sum = 0L; // sum of modCounts
+ for (int j = 0; j < segments.length; j++) {
+ Segment<K,V> segment;
+ if (retries == RETRIES_BEFORE_LOCK) {
+ segment = ensureSegment(j);
+ segment.lock();
+ lockCount++;
+ } else {
+ segment = segmentAt(segments, j);
+ if (segment == null)
+ continue;
+ }
+ HashEntry<K,V>[] tab = segment.table;
+ if (tab != null) {
+ for (int i = 0 ; i < tab.length; i++) {
+ HashEntry<K,V> e;
+ for (e = entryAt(tab, i); e != null; e = e.next) {
+ V v = e.value;
+ if (v != null && value.equals(v))
+ return true;
+ }
+ }
+ sum += segment.modCount;
+ }
}
+ if ((retries >= 0 && sum == previousSum) || lockCount > 0)
+ return false;
+ previousSum = sum;
}
} finally {
- for (int i = 0; i < segments.length; ++i)
- segments[i].unlock();
+ for (int j = 0; j < lockCount; j++)
+ segments[j].unlock();
}
- return found;
}
/**
@@ -850,7 +985,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* full compatibility with class {@link java.util.Hashtable},
* which supported this method prior to introduction of the
* Java Collections framework.
-
+ *
* @param value a value to search for
* @return <tt>true</tt> if and only if some key maps to the
* <tt>value</tt> argument in this table as
@@ -875,11 +1010,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* <tt>null</tt> if there was no mapping for <tt>key</tt>
* @throws NullPointerException if the specified key or value is null
*/
+ @SuppressWarnings("unchecked")
public V put(K key, V value) {
+ Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
- return segmentFor(hash).put(key, hash, value, false);
+ int j = (hash >>> segmentShift) & segmentMask;
+ if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
+ (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
+ s = ensureSegment(j);
+ return s.put(key, hash, value, false);
}
/**
@@ -889,11 +1030,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* or <tt>null</tt> if there was no mapping for the key
* @throws NullPointerException if the specified key or value is null
*/
+ @SuppressWarnings("unchecked")
public V putIfAbsent(K key, V value) {
+ Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
- return segmentFor(hash).put(key, hash, value, true);
+ int j = (hash >>> segmentShift) & segmentMask;
+ if ((s = (Segment<K,V>)UNSAFE.getObject
+ (segments, (j << SSHIFT) + SBASE)) == null)
+ s = ensureSegment(j);
+ return s.put(key, hash, value, true);
}
/**
@@ -919,7 +1066,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
*/
public V remove(Object key) {
int hash = hash(key.hashCode());
- return segmentFor(hash).remove(key, hash, null);
+ Segment<K,V> s = segmentForHash(hash);
+ return s == null ? null : s.remove(key, hash, null);
}
/**
@@ -929,9 +1077,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
*/
public boolean remove(Object key, Object value) {
int hash = hash(key.hashCode());
- if (value == null)
- return false;
- return segmentFor(hash).remove(key, hash, value) != null;
+ Segment<K,V> s;
+ return value != null && (s = segmentForHash(hash)) != null &&
+ s.remove(key, hash, value) != null;
}
/**
@@ -940,10 +1088,11 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
+ int hash = hash(key.hashCode());
if (oldValue == null || newValue == null)
throw new NullPointerException();
- int hash = hash(key.hashCode());
- return segmentFor(hash).replace(key, hash, oldValue, newValue);
+ Segment<K,V> s = segmentForHash(hash);
+ return s != null && s.replace(key, hash, oldValue, newValue);
}
/**
@@ -954,18 +1103,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
+ int hash = hash(key.hashCode());
if (value == null)
throw new NullPointerException();
- int hash = hash(key.hashCode());
- return segmentFor(hash).replace(key, hash, value);
+ Segment<K,V> s = segmentForHash(hash);
+ return s == null ? null : s.replace(key, hash, value);
}
/**
* Removes all of the mappings from this map.
*/
public void clear() {
- for (int i = 0; i < segments.length; ++i)
- segments[i].clear();
+ final Segment<K,V>[] segments = this.segments;
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> s = segmentAt(segments, j);
+ if (s != null)
+ s.clear();
+ }
}
/**
@@ -1066,42 +1220,41 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
advance();
}
- public boolean hasMoreElements() { return hasNext(); }
-
+ /**
+ * Sets nextEntry to first node of next non-empty table
+ * (in backwards order, to simplify checks).
+ */
final void advance() {
- if (nextEntry != null && (nextEntry = nextEntry.next) != null)
- return;
-
- while (nextTableIndex >= 0) {
- if ( (nextEntry = currentTable[nextTableIndex--]) != null)
- return;
- }
-
- while (nextSegmentIndex >= 0) {
- Segment<K,V> seg = segments[nextSegmentIndex--];
- if (seg.count != 0) {
- currentTable = seg.table;
- for (int j = currentTable.length - 1; j >= 0; --j) {
- if ( (nextEntry = currentTable[j]) != null) {
- nextTableIndex = j - 1;
- return;
- }
- }
+ for (;;) {
+ if (nextTableIndex >= 0) {
+ if ((nextEntry = entryAt(currentTable,
+ nextTableIndex--)) != null)
+ break;
+ }
+ else if (nextSegmentIndex >= 0) {
+ Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--);
+ if (seg != null && (currentTable = seg.table) != null)
+ nextTableIndex = currentTable.length - 1;
}
+ else
+ break;
}
}
- public boolean hasNext() { return nextEntry != null; }
-
- HashEntry<K,V> nextEntry() {
- if (nextEntry == null)
+ final HashEntry<K,V> nextEntry() {
+ HashEntry<K,V> e = nextEntry;
+ if (e == null)
throw new NoSuchElementException();
- lastReturned = nextEntry;
- advance();
- return lastReturned;
+ lastReturned = e; // cannot assign until after null check
+ if ((nextEntry = e.next) == null)
+ advance();
+ return e;
}
- public void remove() {
+ public final boolean hasNext() { return nextEntry != null; }
+ public final boolean hasMoreElements() { return nextEntry != null; }
+
+ public final void remove() {
if (lastReturned == null)
throw new IllegalStateException();
ConcurrentHashMap.this.remove(lastReturned.key);
@@ -1113,16 +1266,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
extends HashIterator
implements Iterator<K>, Enumeration<K>
{
- public K next() { return super.nextEntry().key; }
- public K nextElement() { return super.nextEntry().key; }
+ public final K next() { return super.nextEntry().key; }
+ public final K nextElement() { return super.nextEntry().key; }
}
final class ValueIterator
extends HashIterator
implements Iterator<V>, Enumeration<V>
{
- public V next() { return super.nextEntry().value; }
- public V nextElement() { return super.nextEntry().value; }
+ public final V next() { return super.nextEntry().value; }
+ public final V nextElement() { return super.nextEntry().value; }
}
/**
@@ -1137,7 +1290,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
/**
- * Set our entry's value and write through to the map. The
+ * Sets our entry's value and writes through to the map. The
* value to return is somewhat arbitrary here. Since a
* WriteThroughEntry does not necessarily track asynchronous
* changes, the most recent "previous" value could be
@@ -1233,24 +1386,30 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
/* ---------------- Serialization Support -------------- */
/**
- * Save the state of the <tt>ConcurrentHashMap</tt> instance to a
- * stream (i.e., serialize it).
+ * Saves the state of the <tt>ConcurrentHashMap</tt> instance to a
+ * stream (i.e., serializes it).
* @param s the stream
* @serialData
* the key (Object) and value (Object)
* for each key-value mapping, followed by a null pair.
* The key-value mappings are emitted in no particular order.
*/
- private void writeObject(java.io.ObjectOutputStream s) throws IOException {
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // force all segments for serialization compatibility
+ for (int k = 0; k < segments.length; ++k)
+ ensureSegment(k);
s.defaultWriteObject();
+ final Segment<K,V>[] segments = this.segments;
for (int k = 0; k < segments.length; ++k) {
- Segment<K,V> seg = segments[k];
+ Segment<K,V> seg = segmentAt(segments, k);
seg.lock();
try {
HashEntry<K,V>[] tab = seg.table;
for (int i = 0; i < tab.length; ++i) {
- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ HashEntry<K,V> e;
+ for (e = entryAt(tab, i); e != null; e = e.next) {
s.writeObject(e.key);
s.writeObject(e.value);
}
@@ -1264,17 +1423,24 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
/**
- * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a
- * stream (i.e., deserialize it).
+ * Reconstitutes the <tt>ConcurrentHashMap</tt> instance from a
+ * stream (i.e., deserializes it).
* @param s the stream
*/
+ @SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
- // Initialize each segment to be minimally sized, and let grow.
- for (int i = 0; i < segments.length; ++i) {
- segments[i].setTable(new HashEntry[1]);
+ // Re-initialize segments to be minimally sized, and let grow.
+ int cap = MIN_SEGMENT_TABLE_CAPACITY;
+ final Segment<K,V>[] segments = this.segments;
+ for (int k = 0; k < segments.length; ++k) {
+ Segment<K,V> seg = segments[k];
+ if (seg != null) {
+ seg.threshold = (int)(cap * seg.loadFactor);
+ seg.table = (HashEntry<K,V>[]) new HashEntry<?,?>[cap];
+ }
}
// Read the keys and values, and put the mappings in the table
@@ -1286,4 +1452,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
put(key, value);
}
}
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long SBASE;
+ private static final int SSHIFT;
+ private static final long TBASE;
+ private static final int TSHIFT;
+
+ static {
+ int ss, ts;
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> tc = HashEntry[].class;
+ Class<?> sc = Segment[].class;
+ TBASE = UNSAFE.arrayBaseOffset(tc);
+ SBASE = UNSAFE.arrayBaseOffset(sc);
+ ts = UNSAFE.arrayIndexScale(tc);
+ ss = UNSAFE.arrayIndexScale(sc);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);
+ TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
index 72c2e08..30adb36 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea and Martin Buchholz with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -34,10 +34,17 @@ import java.util.Queue;
* ConcurrentModificationException}, and may proceed concurrently with
* other operations.
*
- * <p>Beware that, unlike in most collections, the {@code size}
- * method is <em>NOT</em> a constant-time operation. Because of the
+ * <p>Beware that, unlike in most collections, the {@code size} method
+ * is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these deques, determining the current number
- * of elements requires a traversal of the elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Deque} and {@link Iterator} interfaces.
@@ -245,13 +252,6 @@ public class ConcurrentLinkedDeque<E>
private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;
- static {
- PREV_TERMINATOR = new Node<Object>(null);
- PREV_TERMINATOR.next = PREV_TERMINATOR;
- NEXT_TERMINATOR = new Node<Object>(null);
- NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
- }
-
@SuppressWarnings("unchecked")
Node<E> prevTerminator() {
return (Node<E>) PREV_TERMINATOR;
@@ -267,6 +267,9 @@ public class ConcurrentLinkedDeque<E>
volatile E item;
volatile Node<E> next;
+ Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
+ }
+
/**
* Constructs a new node. Uses relaxed write because item can
* only be seen after publication via casNext or casPrev.
@@ -297,14 +300,25 @@ public class ConcurrentLinkedDeque<E>
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long prevOffset =
- objectFieldOffset(UNSAFE, "prev", Node.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", Node.class);
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long prevOffset;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ prevOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("prev"));
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -1209,8 +1223,7 @@ public class ConcurrentLinkedDeque<E>
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1395,14 +1408,6 @@ public class ConcurrentLinkedDeque<E>
initHeadTail(h, t);
}
- // Unsafe mechanics
-
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentLinkedDeque.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedDeque.class);
private boolean casHead(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
@@ -1412,15 +1417,25 @@ public class ConcurrentLinkedDeque<E>
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ static {
+ PREV_TERMINATOR = new Node<Object>();
+ PREV_TERMINATOR.next = PREV_TERMINATOR;
+ NEXT_TERMINATOR = new Node<Object>();
+ NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentLinkedDeque.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index 3ed0a7c..a0a26fd 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea and Martin Buchholz with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -47,7 +47,14 @@ import java.util.Queue;
* <p>Beware that, unlike in most collections, the {@code size} method
* is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these queues, determining the current number
- * of elements requires a traversal of the elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Queue} and {@link Iterator} interfaces.
@@ -165,12 +172,22 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", Node.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -563,8 +580,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -761,14 +777,6 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
throw new NullPointerException();
}
- // Unsafe mechanics
-
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentLinkedQueue.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedQueue.class);
-
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
@@ -777,15 +785,21 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentLinkedQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
index 2daebc5..3405acf 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -32,11 +32,12 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* If the specified key is not already associated
* with a value, associate it with the given value.
* This is equivalent to
- * <pre>
- * if (!map.containsKey(key))
- * return map.put(key, value);
- * else
- * return map.get(key);</pre>
+ * <pre> {@code
+ * if (!map.containsKey(key))
+ * return map.put(key, value);
+ * else
+ * return map.get(key);}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is to be associated
@@ -61,11 +62,13 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
/**
* Removes the entry for a key only if currently mapped to a given value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key) &amp;&amp; map.get(key).equals(value)) {
- * map.remove(key);
- * return true;
- * } else return false;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(value)) {
+ * map.remove(key);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
@@ -74,20 +77,24 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this map
* @throws ClassCastException if the key or value is of an inappropriate
- * type for this map (optional)
+ * type for this map
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key or value is null,
- * and this map does not permit null keys or values (optional)
+ * and this map does not permit null keys or values
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object key, Object value);
/**
* Replaces the entry for a key only if currently mapped to a given value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key) &amp;&amp; map.get(key).equals(oldValue)) {
- * map.put(key, newValue);
- * return true;
- * } else return false;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
@@ -108,10 +115,12 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
/**
* Replaces the entry for a key only if currently mapped to some value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key)) {
- * return map.put(key, value);
- * } else return null;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else
+ * return null;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
index 7d86afb..ea99886 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
@@ -1,20 +1,20 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* A {@link ConcurrentMap} supporting {@link NavigableMap} operations,
* and recursively so for its navigable sub-maps.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index fbd1083..d0d6f14 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -1,12 +1,15 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
-import java.util.concurrent.atomic.*;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A scalable concurrent {@link ConcurrentNavigableMap} implementation.
@@ -15,8 +18,8 @@ import java.util.concurrent.atomic.*;
* creation time, depending on which constructor is used.
*
* <p>This class implements a concurrent variant of <a
- * href="http://www.cs.umd.edu/~pugh/">SkipLists</a> providing
- * expected average <i>log(n)</i> time cost for the
+ * href="http://en.wikipedia.org/wiki/Skip_list" target="_top">SkipLists</a>
+ * providing expected average <i>log(n)</i> time cost for the
* <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and
* <tt>remove</tt> operations and their variants. Insertion, removal,
* update, and access operations safely execute concurrently by
@@ -37,12 +40,13 @@ import java.util.concurrent.atomic.*;
* <p>Beware that, unlike in most collections, the <tt>size</tt>
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these maps, determining the current number
- * of elements requires a traversal of the elements. Additionally,
- * the bulk operations <tt>putAll</tt>, <tt>equals</tt>, and
- * <tt>clear</tt> are <em>not</em> guaranteed to be performed
- * atomically. For example, an iterator operating concurrently with a
- * <tt>putAll</tt> operation might view only some of the added
- * elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations <tt>putAll</tt>, <tt>equals</tt>,
+ * <tt>toArray</tt>, <tt>containsValue</tt>, and <tt>clear</tt> are
+ * <em>not</em> guaranteed to be performed atomically. For example, an
+ * iterator operating concurrently with a <tt>putAll</tt> operation
+ * might view only some of the added elements.
*
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
@@ -51,10 +55,6 @@ import java.util.concurrent.atomic.*;
* null return values cannot be reliably distinguished from the absence of
* elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
@@ -322,11 +322,11 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
private transient int randomSeed;
/** Lazily initialized key set */
- private transient KeySet keySet;
+ private transient KeySet<K> keySet;
/** Lazily initialized entry set */
- private transient EntrySet entrySet;
+ private transient EntrySet<K,V> entrySet;
/** Lazily initialized values collection */
- private transient Values values;
+ private transient Values<V> values;
/** Lazily initialized descending key set */
private transient ConcurrentNavigableMap<K,V> descendingMap;
@@ -478,13 +478,24 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
}
- // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long valueOffset =
- objectFieldOffset(UNSAFE, "value", Node.class);
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
+ // UNSAFE mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long valueOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ valueOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("value"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/* ---------------- Indexing -------------- */
@@ -551,10 +562,18 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long rightOffset =
- objectFieldOffset(UNSAFE, "right", Index.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long rightOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Index.class;
+ rightOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("right"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/* ---------------- Head nodes -------------- */
@@ -884,7 +903,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* direction.
*/
level = max + 1;
- Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1];
+ Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1];
Index<K,V> idx = null;
for (int i = 1; i <= level; ++i)
idxs[i] = idx = new Index<K,V>(z, idx, null);
@@ -1387,16 +1406,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* @return a shallow copy of this map
*/
public ConcurrentSkipListMap<K,V> clone() {
- ConcurrentSkipListMap<K,V> clone = null;
try {
- clone = (ConcurrentSkipListMap<K,V>) super.clone();
+ @SuppressWarnings("unchecked")
+ ConcurrentSkipListMap<K,V> clone =
+ (ConcurrentSkipListMap<K,V>) super.clone();
+ clone.initialize();
+ clone.buildFromSorted(this);
+ return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
-
- clone.initialize();
- clone.buildFromSorted(this);
- return clone;
}
/**
@@ -1458,7 +1477,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
/* ---------------- Serialization -------------- */
/**
- * Save the state of this map to a stream.
+ * Saves the state of this map to a stream (that is, serializes it).
*
* @serialData The key (Object) and value (Object) for each
* key-value mapping represented by the map, followed by
@@ -1483,7 +1502,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
}
/**
- * Reconstitute the map from a stream.
+ * Reconstitutes the map from a stream (that is, deserializes it).
+ *
+ * @param s the stream
*/
private void readObject(final java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1613,7 +1634,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
* specified value. This operation requires time linear in the
- * map size.
+ * map size. Additionally, it is possible for the map to change
+ * during execution of this method, in which case the returned
+ * result may be inaccurate.
*
* @param value value whose presence in this map is to be tested
* @return <tt>true</tt> if a mapping to <tt>value</tt> exists;
@@ -1703,14 +1726,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
*
* @return a navigable set view of the keys in this map
*/
- public NavigableSet<K> keySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
+ public NavigableSet<K> keySet() {
+ KeySet<K> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<K>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
+ KeySet<K> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<K>(this));
}
/**
@@ -1732,8 +1755,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* reflect any modifications subsequent to construction.
*/
public Collection<V> values() {
- Values vs = values;
- return (vs != null) ? vs : (values = new Values(this));
+ Values<V> vs = values;
+ return (vs != null) ? vs : (values = new Values<V>(this));
}
/**
@@ -1761,8 +1784,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* sorted in ascending key order
*/
public Set<Map.Entry<K,V>> entrySet() {
- EntrySet es = entrySet;
- return (es != null) ? es : (entrySet = new EntrySet(this));
+ EntrySet<K,V> es = entrySet;
+ return (es != null) ? es : (entrySet = new EntrySet<K,V>(this));
}
public ConcurrentNavigableMap<K,V> descendingMap() {
@@ -2253,8 +2276,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
static final class KeySet<E>
extends AbstractSet<E> implements NavigableSet<E> {
- private final ConcurrentNavigableMap<E,Object> m;
- KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; }
+ private final ConcurrentNavigableMap<E,?> m;
+ KeySet(ConcurrentNavigableMap<E,?> map) { m = map; }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
@@ -2268,11 +2291,11 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
public E first() { return m.firstKey(); }
public E last() { return m.lastKey(); }
public E pollFirst() {
- Map.Entry<E,Object> e = m.pollFirstEntry();
+ Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
public E pollLast() {
- Map.Entry<E,Object> e = m.pollLastEntry();
+ Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
public Iterator<E> iterator() {
@@ -2323,20 +2346,20 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
return tailSet(fromElement, true);
}
public NavigableSet<E> descendingSet() {
- return new KeySet(m.descendingMap());
+ return new KeySet<E>(m.descendingMap());
}
}
static final class Values<E> extends AbstractCollection<E> {
- private final ConcurrentNavigableMap<Object, E> m;
- Values(ConcurrentNavigableMap<Object, E> map) {
+ private final ConcurrentNavigableMap<?, E> m;
+ Values(ConcurrentNavigableMap<?, E> map) {
m = map;
}
public Iterator<E> iterator() {
if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<Object,E>)m).valueIterator();
+ return ((ConcurrentSkipListMap<?,E>)m).valueIterator();
else
- return ((SubMap<Object,E>)m).valueIterator();
+ return ((SubMap<?,E>)m).valueIterator();
}
public boolean isEmpty() {
return m.isEmpty();
@@ -2370,14 +2393,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
V1 v = m.get(e.getKey());
return v != null && v.equals(e.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return m.remove(e.getKey(),
e.getValue());
}
@@ -2517,9 +2540,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
if (lo == null)
return m.findFirst();
else if (loInclusive)
- return m.findNear(lo, m.GT|m.EQ);
+ return m.findNear(lo, GT|EQ);
else
- return m.findNear(lo, m.GT);
+ return m.findNear(lo, GT);
}
/**
@@ -2530,9 +2553,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
if (hi == null)
return m.findLast();
else if (hiInclusive)
- return m.findNear(hi, m.LT|m.EQ);
+ return m.findNear(hi, LT|EQ);
else
- return m.findNear(hi, m.LT);
+ return m.findNear(hi, LT);
}
/**
@@ -2614,15 +2637,15 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
*/
private Map.Entry<K,V> getNearEntry(K key, int rel) {
if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
+ if ((rel & LT) == 0)
+ rel |= LT;
else
- rel &= ~m.LT;
+ rel &= ~LT;
}
if (tooLow(key))
- return ((rel & m.LT) != 0) ? null : lowestEntry();
+ return ((rel & LT) != 0) ? null : lowestEntry();
if (tooHigh(key))
- return ((rel & m.LT) != 0) ? highestEntry() : null;
+ return ((rel & LT) != 0) ? highestEntry() : null;
for (;;) {
Node<K,V> n = m.findNear(key, rel);
if (n == null || !inBounds(n.key))
@@ -2637,13 +2660,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
// Almost the same as getNearEntry, except for keys
private K getNearKey(K key, int rel) {
if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
+ if ((rel & LT) == 0)
+ rel |= LT;
else
- rel &= ~m.LT;
+ rel &= ~LT;
}
if (tooLow(key)) {
- if ((rel & m.LT) == 0) {
+ if ((rel & LT) == 0) {
ConcurrentSkipListMap.Node<K,V> n = loNode();
if (isBeforeEnd(n))
return n.key;
@@ -2651,7 +2674,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
return null;
}
if (tooHigh(key)) {
- if ((rel & m.LT) != 0) {
+ if ((rel & LT) != 0) {
ConcurrentSkipListMap.Node<K,V> n = hiNode();
if (n != null) {
K last = n.key;
@@ -2683,7 +2706,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
public V get(Object key) {
if (key == null) throw new NullPointerException();
K k = (K)key;
- return ((!inBounds(k)) ? null : m.get(k));
+ return (!inBounds(k)) ? null : m.get(k);
}
public V put(K key, V value) {
@@ -2850,35 +2873,35 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
/* ---------------- Relational methods -------------- */
public Map.Entry<K,V> ceilingEntry(K key) {
- return getNearEntry(key, (m.GT|m.EQ));
+ return getNearEntry(key, GT|EQ);
}
public K ceilingKey(K key) {
- return getNearKey(key, (m.GT|m.EQ));
+ return getNearKey(key, GT|EQ);
}
public Map.Entry<K,V> lowerEntry(K key) {
- return getNearEntry(key, (m.LT));
+ return getNearEntry(key, LT);
}
public K lowerKey(K key) {
- return getNearKey(key, (m.LT));
+ return getNearKey(key, LT);
}
public Map.Entry<K,V> floorEntry(K key) {
- return getNearEntry(key, (m.LT|m.EQ));
+ return getNearEntry(key, LT|EQ);
}
public K floorKey(K key) {
- return getNearKey(key, (m.LT|m.EQ));
+ return getNearKey(key, LT|EQ);
}
public Map.Entry<K,V> higherEntry(K key) {
- return getNearEntry(key, (m.GT));
+ return getNearEntry(key, GT);
}
public K higherKey(K key) {
- return getNearKey(key, (m.GT));
+ return getNearKey(key, GT);
}
public K firstKey() {
@@ -2909,22 +2932,22 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
public NavigableSet<K> keySet() {
KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
+ return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
}
public NavigableSet<K> navigableKeySet() {
KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
+ return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
}
public Collection<V> values() {
Collection<V> vs = valuesView;
- return (vs != null) ? vs : (valuesView = new Values(this));
+ return (vs != null) ? vs : (valuesView = new Values<V>(this));
}
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es = entrySetView;
- return (es != null) ? es : (entrySetView = new EntrySet(this));
+ return (es != null) ? es : (entrySetView = new EntrySet<K,V>(this));
}
public NavigableSet<K> descendingKeySet() {
@@ -3053,20 +3076,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentSkipListMap.class);
-
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentSkipListMap.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
index d24876f..71431a9 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
@@ -1,12 +1,15 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
-import sun.misc.Unsafe;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A scalable concurrent {@link NavigableSet} implementation based on
@@ -29,12 +32,14 @@ import sun.misc.Unsafe;
* <p>Beware that, unlike in most collections, the <tt>size</tt>
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these sets, determining the current number
- * of elements requires a traversal of the elements. Additionally, the
- * bulk operations <tt>addAll</tt>, <tt>removeAll</tt>,
- * <tt>retainAll</tt>, and <tt>containsAll</tt> are <em>not</em>
- * guaranteed to be performed atomically. For example, an iterator
- * operating concurrently with an <tt>addAll</tt> operation might view
- * only some of the added elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations <tt>addAll</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, <tt>containsAll</tt>,
+ * <tt>equals</tt>, and <tt>toArray</tt> are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an <tt>addAll</tt> operation might view only some
+ * of the added elements.
*
* <p>This class and its iterators implement all of the
* <em>optional</em> methods of the {@link Set} and {@link Iterator}
@@ -43,10 +48,6 @@ import sun.misc.Unsafe;
* because <tt>null</tt> arguments and return values cannot be reliably
* distinguished from the absence of elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <E> the type of elements maintained by this set
* @since 1.6
@@ -127,15 +128,15 @@ public class ConcurrentSkipListSet<E>
* @return a shallow copy of this set
*/
public ConcurrentSkipListSet<E> clone() {
- ConcurrentSkipListSet<E> clone = null;
try {
- clone = (ConcurrentSkipListSet<E>) super.clone();
- clone.setMap(new ConcurrentSkipListMap(m));
+ @SuppressWarnings("unchecked")
+ ConcurrentSkipListSet<E> clone =
+ (ConcurrentSkipListSet<E>) super.clone();
+ clone.setMap(new ConcurrentSkipListMap<E,Object>(m));
+ return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
-
- return clone;
}
/* ---------------- Set operations -------------- */
@@ -291,8 +292,8 @@ public class ConcurrentSkipListSet<E>
public boolean removeAll(Collection<?> c) {
// Override AbstractSet version to avoid unnecessary call to size()
boolean modified = false;
- for (Iterator<?> i = c.iterator(); i.hasNext(); )
- if (remove(i.next()))
+ for (Object e : c)
+ if (remove(e))
modified = true;
return modified;
}
@@ -437,20 +438,24 @@ public class ConcurrentSkipListSet<E>
* @return a reverse order view of this set
*/
public NavigableSet<E> descendingSet() {
- return new ConcurrentSkipListSet(m.descendingMap());
+ return new ConcurrentSkipListSet<E>(m.descendingMap());
}
// Support for resetting map in clone
- private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private void setMap(ConcurrentNavigableMap<E,Object> map) {
+ UNSAFE.putObjectVolatile(this, mapOffset, map);
+ }
+
+ private static final sun.misc.Unsafe UNSAFE;
private static final long mapOffset;
static {
try {
- mapOffset = unsafe.objectFieldOffset
- (ConcurrentSkipListSet.class.getDeclaredField("m"));
- } catch (Exception ex) { throw new Error(ex); }
- }
- private void setMap(ConcurrentNavigableMap<E,Object> map) {
- unsafe.putObjectVolatile(this, mapOffset, map);
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentSkipListSet.class;
+ mapOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("m"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
index 87419fc..1f37bc9 100644
--- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -160,8 +160,7 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E>
* The following code can be used to dump the set into a newly allocated
* array of <tt>String</tt>:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -358,6 +357,6 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E>
* Test for equality, coping with nulls.
*/
private static boolean eq(Object o1, Object o2) {
- return (o1 == null ? o2 == null : o1.equals(o2));
+ return (o1 == null) ? o2 == null : o1.equals(o2);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CountDownLatch.java b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
index 1888279..e90badf 100644
--- a/luni/src/main/java/java/util/concurrent/CountDownLatch.java
+++ b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
-import java.util.concurrent.atomic.*;
/**
* A synchronization aid that allows one or more threads to wait until
@@ -44,7 +43,7 @@ import java.util.concurrent.atomic.*;
* until all workers have completed.
* </ul>
*
- * <pre>
+ * <pre> {@code
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
@@ -76,9 +75,7 @@ import java.util.concurrent.atomic.*;
* }
*
* void doWork() { ... }
- * }
- *
- * </pre>
+ * }}</pre>
*
* <p>Another typical usage would be to divide a problem into N parts,
* describe each part with a Runnable that executes that portion and
@@ -87,7 +84,7 @@ import java.util.concurrent.atomic.*;
* will be able to pass through await. (When threads must repeatedly
* count down in this way, instead use a {@link CyclicBarrier}.)
*
- * <pre>
+ * <pre> {@code
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
@@ -115,9 +112,7 @@ import java.util.concurrent.atomic.*;
* }
*
* void doWork() { ... }
- * }
- *
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: Until the count reaches
* zero, actions in a thread prior to calling
diff --git a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
index d5738c5..cf0b46e 100644
--- a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
+++ b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,7 +23,8 @@ import java.util.concurrent.locks.*;
*
* <p><b>Sample usage:</b> Here is an example of
* using a barrier in a parallel decomposition design:
- * <pre>
+ *
+ * <pre> {@code
* class Solver {
* final int N;
* final float[][] data;
@@ -61,8 +62,8 @@ import java.util.concurrent.locks.*;
*
* waitUntilDone();
* }
- * }
- * </pre>
+ * }}</pre>
+ *
* Here, each worker thread processes a row of the matrix then waits at the
* barrier until all rows have been processed. When all rows are processed
* the supplied {@link Runnable} barrier action is executed and merges the
@@ -76,9 +77,10 @@ import java.util.concurrent.locks.*;
* {@link #await} returns the arrival index of that thread at the barrier.
* You can then choose which thread should execute the barrier action, for
* example:
- * <pre> if (barrier.await() == 0) {
- * // log the completion of this iteration
- * }</pre>
+ * <pre> {@code
+ * if (barrier.await() == 0) {
+ * // log the completion of this iteration
+ * }}</pre>
*
* <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model
* for failed synchronization attempts: If a thread leaves a barrier
@@ -175,21 +177,21 @@ public class CyclicBarrier {
throw new InterruptedException();
}
- int index = --count;
- if (index == 0) { // tripped
- boolean ranAction = false;
- try {
- final Runnable command = barrierCommand;
- if (command != null)
- command.run();
- ranAction = true;
- nextGeneration();
- return 0;
- } finally {
- if (!ranAction)
- breakBarrier();
- }
- }
+ int index = --count;
+ if (index == 0) { // tripped
+ boolean ranAction = false;
+ try {
+ final Runnable command = barrierCommand;
+ if (command != null)
+ command.run();
+ ranAction = true;
+ nextGeneration();
+ return 0;
+ } finally {
+ if (!ranAction)
+ breakBarrier();
+ }
+ }
// loop until tripped, broken, interrupted, or timed out
for (;;) {
@@ -325,7 +327,7 @@ public class CyclicBarrier {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
- throw new Error(toe); // cannot happen;
+ throw new Error(toe); // cannot happen
}
}
diff --git a/luni/src/main/java/java/util/concurrent/DelayQueue.java b/luni/src/main/java/java/util/concurrent/DelayQueue.java
index 8c44e82..52028cb 100644
--- a/luni/src/main/java/java/util/concurrent/DelayQueue.java
+++ b/luni/src/main/java/java/util/concurrent/DelayQueue.java
@@ -1,12 +1,14 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
// BEGIN android-note
@@ -29,7 +31,9 @@ import java.util.*;
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
- * Iterator} interfaces.
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the DelayQueue in any particular order.
*
* @since 1.5
* @author Doug Lea
@@ -154,7 +158,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
lock.lock();
try {
E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
@@ -179,7 +183,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
if (first == null)
available.await();
else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
else if (leader != null)
@@ -226,7 +230,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
if (nanos <= 0)
@@ -284,6 +288,17 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
}
/**
+ * Return first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private E peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ E first = q.peek();
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
+ }
+
+ /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@@ -298,11 +313,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
lock.lock();
try {
int n = 0;
- for (;;) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -328,11 +341,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
lock.lock();
try {
int n = 0;
- while (n < maxElements) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; n < maxElements && (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -411,8 +422,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
* <p>The following code can be used to dump a delay queue into a newly
* allocated array of <tt>Delayed</tt>:
*
- * <pre>
- * Delayed[] a = q.toArray(new Delayed[0]);</pre>
+ * <pre> {@code Delayed[] a = q.toArray(new Delayed[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -451,6 +461,24 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
}
/**
+ * Identity-based version for use in Itr.remove
+ */
+ void removeEQ(Object o) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ for (Iterator<E> it = q.iterator(); it.hasNext(); ) {
+ if (o == it.next()) {
+ it.remove();
+ break;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
* Returns an iterator over all the elements (both expired and
* unexpired) in this queue. The iterator does not return the
* elements in any particular order.
@@ -473,7 +501,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
*/
private class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
- int cursor; // index of next element to return;
+ int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
@@ -496,21 +524,8 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
- Object x = array[lastRet];
+ removeEQ(array[lastRet]);
lastRet = -1;
- // Traverse underlying queue to find == element,
- // not just a .equals element.
- lock.lock();
- try {
- for (Iterator it = q.iterator(); it.hasNext(); ) {
- if (it.next() == x) {
- it.remove();
- return;
- }
- }
- } finally {
- lock.unlock();
- }
}
}
diff --git a/luni/src/main/java/java/util/concurrent/Delayed.java b/luni/src/main/java/java/util/concurrent/Delayed.java
index b1ff4ee..39d927c 100644
--- a/luni/src/main/java/java/util/concurrent/Delayed.java
+++ b/luni/src/main/java/java/util/concurrent/Delayed.java
@@ -1,13 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.*;
-
/**
* A mix-in style interface for marking objects that should be
* acted upon after a given delay.
diff --git a/luni/src/main/java/java/util/concurrent/Exchanger.java b/luni/src/main/java/java/util/concurrent/Exchanger.java
index 3c230be..6069dce 100644
--- a/luni/src/main/java/java/util/concurrent/Exchanger.java
+++ b/luni/src/main/java/java/util/concurrent/Exchanger.java
@@ -2,7 +2,7 @@
* Written by Doug Lea, Bill Scherer, and Michael Scott with
* assistance from members of JCP JSR-166 Expert Group and released to
* the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,7 +23,7 @@ import java.util.concurrent.locks.LockSupport;
* to swap buffers between threads so that the thread filling the
* buffer gets a freshly emptied one when it needs it, handing off the
* filled one to the thread emptying the buffer.
- * <pre>{@code
+ * <pre> {@code
* class FillAndEmpty {
* Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
* DataBuffer initialEmptyBuffer = ... a made-up type
@@ -59,8 +59,7 @@ import java.util.concurrent.locks.LockSupport;
* new Thread(new FillingLoop()).start();
* new Thread(new EmptyingLoop()).start();
* }
- * }
- * }</pre>
+ * }}</pre>
*
* <p>Memory consistency effects: For each pair of threads that
* successfully exchange objects via an {@code Exchanger}, actions
@@ -135,8 +134,8 @@ public class Exchanger<V> {
* races between two threads or thread pre-emptions occurring
* between reading and CASing. Also, very transient peak
* contention can be much higher than the average sustainable
- * levels. The max limit is decreased on average 50% of the times
- * that a non-slot-zero wait elapses without being fulfilled.
+ * levels. An attempt to decrease the max limit is usually made
+ * when a non-slot-zero wait elapses without being fulfilled.
* Threads experiencing elapsed waits move closer to zero, so
* eventually find existing (or future) threads even if the table
* has been shrunk due to inactivity. The chosen mechanics and
@@ -275,7 +274,9 @@ public class Exchanger<V> {
* extra space.
*/
private static final class Slot extends AtomicReference<Object> {
- // Improve likelihood of isolation on <= 64 byte cache lines
+ // Improve likelihood of isolation on <= 128 byte cache lines.
+ // We used to target 64 byte cache lines, but some x86s (including
+ // i7 under some BIOSes) actually use 128 byte cache lines.
long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
}
diff --git a/luni/src/main/java/java/util/concurrent/ExecutionException.java b/luni/src/main/java/java/util/concurrent/ExecutionException.java
index bc561e5..9bb8dee 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutionException.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutionException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -50,11 +50,9 @@ public class ExecutionException extends Exception {
/**
* Constructs an <tt>ExecutionException</tt> with the specified cause.
- * The detail message is set to:
- * <pre>
- * (cause == null ? null : cause.toString())</pre>
- * (which typically contains the class and detail message of
- * <tt>cause</tt>).
+ * The detail message is set to {@code (cause == null ? null :
+ * cause.toString())} (which typically contains the class and
+ * detail message of <tt>cause</tt>).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method)
diff --git a/luni/src/main/java/java/util/concurrent/Executor.java b/luni/src/main/java/java/util/concurrent/Executor.java
index fbc4e6f..831bf46 100644
--- a/luni/src/main/java/java/util/concurrent/Executor.java
+++ b/luni/src/main/java/java/util/concurrent/Executor.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -27,23 +27,23 @@ package java.util.concurrent;
* executor can run the submitted task immediately in the caller's
* thread:
*
- * <pre>
+ * <pre> {@code
* class DirectExecutor implements Executor {
- * public void execute(Runnable r) {
- * r.run();
- * }
- * }</pre>
+ * public void execute(Runnable r) {
+ * r.run();
+ * }
+ * }}</pre>
*
* More typically, tasks are executed in some thread other
* than the caller's thread. The executor below spawns a new thread
* for each task.
*
- * <pre>
+ * <pre> {@code
* class ThreadPerTaskExecutor implements Executor {
- * public void execute(Runnable r) {
- * new Thread(r).start();
- * }
- * }</pre>
+ * public void execute(Runnable r) {
+ * new Thread(r).start();
+ * }
+ * }}</pre>
*
* Many <tt>Executor</tt> implementations impose some sort of
* limitation on how and when tasks are scheduled. The executor below
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
index b41955d..c0d6006 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorService.java b/luni/src/main/java/java/util/concurrent/ExecutorService.java
index 89688e4..a33ceec 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorService.java
@@ -1,14 +1,16 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.List;
import java.util.Collection;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
+
+// BEGIN android-note
+// removed security manager docs
+// END android-note
/**
* An {@link Executor} that provides methods to manage termination and
@@ -44,7 +46,7 @@ import java.security.PrivilegedExceptionAction;
* pool service incoming requests. It uses the preconfigured {@link
* Executors#newFixedThreadPool} factory method:
*
- * <pre>
+ * <pre> {@code
* class NetworkService implements Runnable {
* private final ServerSocket serverSocket;
* private final ExecutorService pool;
@@ -72,14 +74,13 @@ import java.security.PrivilegedExceptionAction;
* public void run() {
* // read and service request on socket
* }
- * }
- * </pre>
+ * }}</pre>
*
* The following method shuts down an <tt>ExecutorService</tt> in two phases,
* first by calling <tt>shutdown</tt> to reject incoming tasks, and then
* calling <tt>shutdownNow</tt>, if necessary, to cancel any lingering tasks:
*
- * <pre>
+ * <pre> {@code
* void shutdownAndAwaitTermination(ExecutorService pool) {
* pool.shutdown(); // Disable new tasks from being submitted
* try {
@@ -96,8 +97,7 @@ import java.security.PrivilegedExceptionAction;
* // Preserve interrupt status
* Thread.currentThread().interrupt();
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: Actions in a thread prior to the
* submission of a {@code Runnable} or {@code Callable} task to an
diff --git a/luni/src/main/java/java/util/concurrent/Executors.java b/luni/src/main/java/java/util/concurrent/Executors.java
index e42b0cc..b4f03ba 100644
--- a/luni/src/main/java/java/util/concurrent/Executors.java
+++ b/luni/src/main/java/java/util/concurrent/Executors.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -12,9 +12,10 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
-import java.security.AccessControlException;
-// import sun.security.util.SecurityConstants; // android-removed
+// BEGIN android-note
+// removed security manager docs
+// END android-note
/**
* Factory and utility methods for {@link Executor}, {@link
* ExecutorService}, {@link ScheduledExecutorService}, {@link
@@ -271,10 +272,7 @@ public class Executors {
/**
* Returns a default thread factory used to create new threads.
* This factory creates all new threads used by an Executor in the
- * same {@link ThreadGroup}. If there is a {@link
- * java.lang.SecurityManager}, it uses the group of {@link
- * System#getSecurityManager}, else the group of the thread
- * invoking this <tt>defaultThreadFactory</tt> method. Each new
+ * same {@link ThreadGroup}. Each new
* thread is created as a non-daemon thread with priority set to
* the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum
* priority permitted in the thread group. New threads have names
@@ -289,36 +287,7 @@ public class Executors {
}
/**
- * Returns a thread factory used to create new threads that
- * have the same permissions as the current thread.
- * This factory creates threads with the same settings as {@link
- * Executors#defaultThreadFactory}, additionally setting the
- * AccessControlContext and contextClassLoader of new threads to
- * be the same as the thread invoking this
- * <tt>privilegedThreadFactory</tt> method. A new
- * <tt>privilegedThreadFactory</tt> can be created within an
- * {@link AccessController#doPrivileged} action setting the
- * current thread's access control context to create threads with
- * the selected permission settings holding within that action.
- *
- * <p> Note that while tasks running within such threads will have
- * the same access control and class loader settings as the
- * current thread, they need not have the same {@link
- * java.lang.ThreadLocal} or {@link
- * java.lang.InheritableThreadLocal} values. If necessary,
- * particular values of thread locals can be set or reset before
- * any task runs in {@link ThreadPoolExecutor} subclasses using
- * {@link ThreadPoolExecutor#beforeExecute}. Also, if it is
- * necessary to initialize worker threads to have the same
- * InheritableThreadLocal settings as some other designated
- * thread, you can create a custom ThreadFactory in which that
- * thread waits for and services requests to create others that
- * will inherit its values.
- *
- * @return a thread factory
- * @throws AccessControlException if the current access control
- * context does not have permission to both get and set context
- * class loader.
+ * Legacy security code; do not use.
*/
public static ThreadFactory privilegedThreadFactory() {
return new PrivilegedThreadFactory();
@@ -383,18 +352,7 @@ public class Executors {
}
/**
- * Returns a {@link Callable} object that will, when
- * called, execute the given <tt>callable</tt> under the current
- * access control context. This method should normally be
- * invoked within an {@link AccessController#doPrivileged} action
- * to create callables that will, if possible, execute under the
- * selected permission settings holding within that action; or if
- * not possible, throw an associated {@link
- * AccessControlException}.
- * @param callable the underlying task
- * @return a callable object
- * @throws NullPointerException if callable null
- *
+ * Legacy security code; do not use.
*/
public static <T> Callable<T> privilegedCallable(Callable<T> callable) {
if (callable == null)
@@ -405,20 +363,10 @@ public class Executors {
/**
* Returns a {@link Callable} object that will, when
* called, execute the given <tt>callable</tt> under the current
- * access control context, with the current context class loader
- * as the context class loader. This method should normally be
- * invoked within an {@link AccessController#doPrivileged} action
- * to create callables that will, if possible, execute under the
- * selected permission settings holding within that action; or if
- * not possible, throw an associated {@link
- * AccessControlException}.
- * @param callable the underlying task
+ * with the current context class loader as the context class loader.
*
* @return a callable object
* @throws NullPointerException if callable null
- * @throws AccessControlException if the current access control
- * context does not have permission to both set and get context
- * class loader.
*/
public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) {
if (callable == null)
@@ -480,17 +428,19 @@ public class Executors {
private final ClassLoader ccl;
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // Calls to getContextClassLoader from this class
- // never trigger a security check, but we check
- // whether our callers have this permission anyways.
- sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed
-
- // Whether setContextClassLoader turns out to be necessary
- // or not, we fail fast if permission is not available.
- sm.checkPermission(new RuntimePermission("setContextClassLoader"));
- }
+ // BEGIN android-removed
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm != null) {
+ // // Calls to getContextClassLoader from this class
+ // // never trigger a security check, but we check
+ // // whether our callers have this permission anyways.
+ // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+ //
+ // // Whether setContextClassLoader turns out to be necessary
+ // // or not, we fail fast if permission is not available.
+ // sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ // }
+ // END android-removed
this.task = task;
this.acc = AccessController.getContext();
this.ccl = Thread.currentThread().getContextClassLoader();
@@ -561,16 +511,18 @@ public class Executors {
PrivilegedThreadFactory() {
super();
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // Calls to getContextClassLoader from this class
- // never trigger a security check, but we check
- // whether our callers have this permission anyways.
- sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed
-
- // Fail fast
- sm.checkPermission(new RuntimePermission("setContextClassLoader"));
- }
+ // BEGIN android-removed
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm != null) {
+ // // Calls to getContextClassLoader from this class
+ // // never trigger a security check, but we check
+ // // whether our callers have this permission anyways.
+ // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+ //
+ // // Fail fast
+ // sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ // }
+ // END android-removed
this.acc = AccessController.getContext();
this.ccl = Thread.currentThread().getContextClassLoader();
}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
new file mode 100644
index 0000000..ee15ac8
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -0,0 +1,2127 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Condition;
+import libcore.util.SneakyThrow;
+
+// BEGIN android-note
+// removed security manager docs
+// END android-note
+
+/**
+ * An {@link ExecutorService} for running {@link ForkJoinTask}s.
+ * A {@code ForkJoinPool} provides the entry point for submissions
+ * from non-{@code ForkJoinTask} clients, as well as management and
+ * monitoring operations.
+ *
+ * <p>A {@code ForkJoinPool} differs from other kinds of {@link
+ * ExecutorService} mainly by virtue of employing
+ * <em>work-stealing</em>: all threads in the pool attempt to find and
+ * execute subtasks created by other active tasks (eventually blocking
+ * waiting for work if none exist). This enables efficient processing
+ * when most tasks spawn other subtasks (as do most {@code
+ * ForkJoinTask}s). When setting <em>asyncMode</em> to true in
+ * constructors, {@code ForkJoinPool}s may also be appropriate for use
+ * with event-style tasks that are never joined.
+ *
+ * <p>A {@code ForkJoinPool} is constructed with a given target
+ * parallelism level; by default, equal to the number of available
+ * processors. The pool attempts to maintain enough active (or
+ * available) threads by dynamically adding, suspending, or resuming
+ * internal worker threads, even if some tasks are stalled waiting to
+ * join others. However, no such adjustments are guaranteed in the
+ * face of blocked IO or other unmanaged synchronization. The nested
+ * {@link ManagedBlocker} interface enables extension of the kinds of
+ * synchronization accommodated.
+ *
+ * <p>In addition to execution and lifecycle control methods, this
+ * class provides status check methods (for example
+ * {@link #getStealCount}) that are intended to aid in developing,
+ * tuning, and monitoring fork/join applications. Also, method
+ * {@link #toString} returns indications of pool state in a
+ * convenient form for informal monitoring.
+ *
+ * <p> As is the case with other ExecutorServices, there are three
+ * main task execution methods summarized in the following
+ * table. These are designed to be used by clients not already engaged
+ * in fork/join computations in the current pool. The main forms of
+ * these methods accept instances of {@code ForkJoinTask}, but
+ * overloaded forms also allow mixed execution of plain {@code
+ * Runnable}- or {@code Callable}- based activities as well. However,
+ * tasks that are already executing in a pool should normally
+ * <em>NOT</em> use these pool execution methods, but instead use the
+ * within-computation forms listed in the table.
+ *
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td>
+ * <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td>
+ * </tr>
+ * <tr>
+ * <td> <b>Arrange async execution</td>
+ * <td> {@link #execute(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#fork}</td>
+ * </tr>
+ * <tr>
+ * <td> <b>Await and obtain result</td>
+ * <td> {@link #invoke(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#invoke}</td>
+ * </tr>
+ * <tr>
+ * <td> <b>Arrange exec and obtain Future</td>
+ * <td> {@link #submit(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td>
+ * </tr>
+ * </table>
+ *
+ * <p><b>Sample Usage.</b> Normally a single {@code ForkJoinPool} is
+ * used for all parallel task execution in a program or subsystem.
+ * Otherwise, use would not usually outweigh the construction and
+ * bookkeeping overhead of creating a large set of threads. For
+ * example, a common pool could be used for the {@code SortTasks}
+ * illustrated in {@link RecursiveAction}. Because {@code
+ * ForkJoinPool} uses threads in {@linkplain java.lang.Thread#isDaemon
+ * daemon} mode, there is typically no need to explicitly {@link
+ * #shutdown} such a pool upon program exit.
+ *
+ * <pre> {@code
+ * static final ForkJoinPool mainPool = new ForkJoinPool();
+ * ...
+ * public void sort(long[] array) {
+ * mainPool.invoke(new SortTask(array, 0, array.length));
+ * }}</pre>
+ *
+ * <p><b>Implementation notes</b>: This implementation restricts the
+ * maximum number of running threads to 32767. Attempts to create
+ * pools with greater than the maximum number result in
+ * {@code IllegalArgumentException}.
+ *
+ * <p>This implementation rejects submitted tasks (that is, by throwing
+ * {@link RejectedExecutionException}) only when the pool is shut down
+ * or internal resources have been exhausted.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ForkJoinPool extends AbstractExecutorService {
+
+ /*
+ * Implementation Overview
+ *
+ * This class provides the central bookkeeping and control for a
+ * set of worker threads: Submissions from non-FJ threads enter
+ * into a submission queue. Workers take these tasks and typically
+ * split them into subtasks that may be stolen by other workers.
+ * Preference rules give first priority to processing tasks from
+ * their own queues (LIFO or FIFO, depending on mode), then to
+ * randomized FIFO steals of tasks in other worker queues, and
+ * lastly to new submissions.
+ *
+ * The main throughput advantages of work-stealing stem from
+ * decentralized control -- workers mostly take tasks from
+ * themselves or each other. We cannot negate this in the
+ * implementation of other management responsibilities. The main
+ * tactic for avoiding bottlenecks is packing nearly all
+ * essentially atomic control state into a single 64bit volatile
+ * variable ("ctl"). This variable is read on the order of 10-100
+ * times as often as it is modified (always via CAS). (There is
+ * some additional control state, for example variable "shutdown"
+ * for which we can cope with uncoordinated updates.) This
+ * streamlines synchronization and control at the expense of messy
+ * constructions needed to repack status bits upon updates.
+ * Updates tend not to contend with each other except during
+ * bursts while submitted tasks begin or end. In some cases when
+ * they do contend, threads can instead do something else
+ * (usually, scan for tasks) until contention subsides.
+ *
+ * To enable packing, we restrict maximum parallelism to (1<<15)-1
+ * (which is far in excess of normal operating range) to allow
+ * ids, counts, and their negations (used for thresholding) to fit
+ * into 16bit fields.
+ *
+ * Recording Workers. Workers are recorded in the "workers" array
+ * that is created upon pool construction and expanded if (rarely)
+ * necessary. This is an array as opposed to some other data
+ * structure to support index-based random steals by workers.
+ * Updates to the array recording new workers and unrecording
+ * terminated ones are protected from each other by a seqLock
+ * (scanGuard) but the array is otherwise concurrently readable,
+ * and accessed directly by workers. To simplify index-based
+ * operations, the array size is always a power of two, and all
+ * readers must tolerate null slots. To avoid flailing during
+ * start-up, the array is presized to hold twice #parallelism
+ * workers (which is unlikely to need further resizing during
+ * execution). But to avoid dealing with so many null slots,
+ * variable scanGuard includes a mask for the nearest power of two
+ * that contains all current workers. All worker thread creation
+ * is on-demand, triggered by task submissions, replacement of
+ * terminated workers, and/or compensation for blocked
+ * workers. However, all other support code is set up to work with
+ * other policies. To ensure that we do not hold on to worker
+ * references that would prevent GC, ALL accesses to workers are
+ * via indices into the workers array (which is one source of some
+ * of the messy code constructions here). In essence, the workers
+ * array serves as a weak reference mechanism. Thus for example
+ * the wait queue field of ctl stores worker indices, not worker
+ * references. Access to the workers in associated methods (for
+ * example signalWork) must both index-check and null-check the
+ * IDs. All such accesses ignore bad IDs by returning out early
+ * from what they are doing, since this can only be associated
+ * with termination, in which case it is OK to give up.
+ *
+ * All uses of the workers array, as well as queue arrays, check
+ * that the array is non-null (even if previously non-null). This
+ * allows nulling during termination, which is currently not
+ * necessary, but remains an option for resource-revocation-based
+ * shutdown schemes.
+ *
+ * Wait Queuing. Unlike HPC work-stealing frameworks, we cannot
+ * let workers spin indefinitely scanning for tasks when none can
+ * be found immediately, and we cannot start/resume workers unless
+ * there appear to be tasks available. On the other hand, we must
+ * quickly prod them into action when new tasks are submitted or
+ * generated. We park/unpark workers after placing in an event
+ * wait queue when they cannot find work. This "queue" is actually
+ * a simple Treiber stack, headed by the "id" field of ctl, plus a
+ * 15bit counter value to both wake up waiters (by advancing their
+ * count) and avoid ABA effects. Successors are held in worker
+ * field "nextWait". Queuing deals with several intrinsic races,
+ * mainly that a task-producing thread can miss seeing (and
+ * signalling) another thread that gave up looking for work but
+ * has not yet entered the wait queue. We solve this by requiring
+ * a full sweep of all workers both before (in scan()) and after
+ * (in tryAwaitWork()) a newly waiting worker is added to the wait
+ * queue. During a rescan, the worker might release some other
+ * queued worker rather than itself, which has the same net
+ * effect. Because enqueued workers may actually be rescanning
+ * rather than waiting, we set and clear the "parked" field of
+ * ForkJoinWorkerThread to reduce unnecessary calls to unpark.
+ * (Use of the parked field requires a secondary recheck to avoid
+ * missed signals.)
+ *
+ * Signalling. We create or wake up workers only when there
+ * appears to be at least one task they might be able to find and
+ * execute. When a submission is added or another worker adds a
+ * task to a queue that previously had two or fewer tasks, they
+ * signal waiting workers (or trigger creation of new ones if
+ * fewer than the given parallelism level -- see signalWork).
+ * These primary signals are buttressed by signals during rescans
+ * as well as those performed when a worker steals a task and
+ * notices that there are more tasks too; together these cover the
+ * signals needed in cases when more than two tasks are pushed
+ * but untaken.
+ *
+ * Trimming workers. To release resources after periods of lack of
+ * use, a worker starting to wait when the pool is quiescent will
+ * time out and terminate if the pool has remained quiescent for
+ * SHRINK_RATE nanosecs. This will slowly propagate, eventually
+ * terminating all workers after long periods of non-use.
+ *
+ * Submissions. External submissions are maintained in an
+ * array-based queue that is structured identically to
+ * ForkJoinWorkerThread queues except for the use of
+ * submissionLock in method addSubmission. Unlike the case for
+ * worker queues, multiple external threads can add new
+ * submissions, so adding requires a lock.
+ *
+ * Compensation. Beyond work-stealing support and lifecycle
+ * control, the main responsibility of this framework is to take
+ * actions when one worker is waiting to join a task stolen (or
+ * always held by) another. Because we are multiplexing many
+ * tasks on to a pool of workers, we can't just let them block (as
+ * in Thread.join). We also cannot just reassign the joiner's
+ * run-time stack with another and replace it later, which would
+ * be a form of "continuation", that even if possible is not
+ * necessarily a good idea since we sometimes need both an
+ * unblocked task and its continuation to progress. Instead we
+ * combine two tactics:
+ *
+ * Helping: Arranging for the joiner to execute some task that it
+ * would be running if the steal had not occurred. Method
+ * ForkJoinWorkerThread.joinTask tracks joining->stealing
+ * links to try to find such a task.
+ *
+ * Compensating: Unless there are already enough live threads,
+ * method tryPreBlock() may create or re-activate a spare
+ * thread to compensate for blocked joiners until they
+ * unblock.
+ *
+ * The ManagedBlocker extension API can't use helping so relies
+ * only on compensation in method awaitBlocker.
+ *
+ * It is impossible to keep exactly the target parallelism number
+ * of threads running at any given time. Determining the
+ * existence of conservatively safe helping targets, the
+ * availability of already-created spares, and the apparent need
+ * to create new spares are all racy and require heuristic
+ * guidance, so we rely on multiple retries of each. Currently,
+ * in keeping with on-demand signalling policy, we compensate only
+ * if blocking would leave less than one active (non-waiting,
+ * non-blocked) worker. Additionally, to avoid some false alarms
+ * due to GC, lagging counters, system activity, etc, compensated
+ * blocking for joins is only attempted after rechecks stabilize
+ * (retries are interspersed with Thread.yield, for good
+ * citizenship). The variable blockedCount, incremented before
+ * blocking and decremented after, is sometimes needed to
+ * distinguish cases of waiting for work vs blocking on joins or
+ * other managed sync. Both cases are equivalent for most pool
+ * control, so we can update non-atomically. (Additionally,
+ * contention on blockedCount alleviates some contention on ctl).
+ *
+ * Shutdown and Termination. A call to shutdownNow atomically sets
+ * the ctl stop bit and then (non-atomically) sets each workers
+ * "terminate" status, cancels all unprocessed tasks, and wakes up
+ * all waiting workers. Detecting whether termination should
+ * commence after a non-abrupt shutdown() call requires more work
+ * and bookkeeping. We need consensus about quiescence (i.e., that
+ * there is no more work) which is reflected in active counts so
+ * long as there are no current blockers, as well as possible
+ * re-evaluations during independent changes in blocking or
+ * quiescing workers.
+ *
+ * Style notes: There is a lot of representation-level coupling
+ * among classes ForkJoinPool, ForkJoinWorkerThread, and
+ * ForkJoinTask. Most fields of ForkJoinWorkerThread maintain
+ * data structures managed by ForkJoinPool, so are directly
+ * accessed. Conversely we allow access to "workers" array by
+ * workers, and direct access to ForkJoinTask.status by both
+ * ForkJoinPool and ForkJoinWorkerThread. There is little point
+ * trying to reduce this, since any associated future changes in
+ * representations will need to be accompanied by algorithmic
+ * changes anyway. All together, these low-level implementation
+ * choices produce as much as a factor of 4 performance
+ * improvement compared to naive implementations, and enable the
+ * processing of billions of tasks per second, at the expense of
+ * some ugliness.
+ *
+ * Methods signalWork() and scan() are the main bottlenecks so are
+ * especially heavily micro-optimized/mangled. There are lots of
+ * inline assignments (of form "while ((local = field) != 0)")
+ * which are usually the simplest way to ensure the required read
+ * orderings (which are sometimes critical). This leads to a
+ * "C"-like style of listing declarations of these locals at the
+ * heads of methods or blocks. There are several occurrences of
+ * the unusual "do {} while (!cas...)" which is the simplest way
+ * to force an update of a CAS'ed variable. There are also other
+ * coding oddities that help some methods perform reasonably even
+ * when interpreted (not compiled).
+ *
+ * The order of declarations in this file is: (1) declarations of
+ * statics (2) fields (along with constants used when unpacking
+ * some of them), listed in an order that tends to reduce
+ * contention among them a bit under most JVMs. (3) internal
+ * control methods (4) callbacks and other support for
+ * ForkJoinTask and ForkJoinWorkerThread classes, (5) exported
+ * methods (plus a few little helpers). (6) static block
+ * initializing all statics in a minimally dependent order.
+ */
+
+ /**
+ * Factory for creating new {@link ForkJoinWorkerThread}s.
+ * A {@code ForkJoinWorkerThreadFactory} must be defined and used
+ * for {@code ForkJoinWorkerThread} subclasses that extend base
+ * functionality or initialize threads with different contexts.
+ */
+ public static interface ForkJoinWorkerThreadFactory {
+ /**
+ * Returns a new worker thread operating in the given pool.
+ *
+ * @param pool the pool this thread works in
+ * @throws NullPointerException if the pool is null
+ */
+ public ForkJoinWorkerThread newThread(ForkJoinPool pool);
+ }
+
+ /**
+ * Default ForkJoinWorkerThreadFactory implementation; creates a
+ * new ForkJoinWorkerThread.
+ */
+ static class DefaultForkJoinWorkerThreadFactory
+ implements ForkJoinWorkerThreadFactory {
+ public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ return new ForkJoinWorkerThread(pool);
+ }
+ }
+
+ /**
+ * Creates a new ForkJoinWorkerThread. This factory is used unless
+ * overridden in ForkJoinPool constructors.
+ */
+ public static final ForkJoinWorkerThreadFactory
+ defaultForkJoinWorkerThreadFactory;
+
+ /**
+ * Permission required for callers of methods that may start or
+ * kill threads.
+ */
+ private static final RuntimePermission modifyThreadPermission;
+
+ /**
+ * If there is a security manager, makes sure caller has
+ * permission to modify threads.
+ */
+ private static void checkPermission() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null)
+ security.checkPermission(modifyThreadPermission);
+ }
+
+ /**
+ * Generator for assigning sequence numbers as pool names.
+ */
+ private static final AtomicInteger poolNumberGenerator;
+
+ /**
+ * Generator for initial random seeds for worker victim
+ * selection. This is used only to create initial seeds. Random
+ * steals use a cheaper xorshift generator per steal attempt. We
+ * don't expect much contention on seedGenerator, so just use a
+ * plain Random.
+ */
+ static final Random workerSeedGenerator;
+
+ /**
+ * Array holding all worker threads in the pool. Initialized upon
+ * construction. Array size must be a power of two. Updates and
+ * replacements are protected by scanGuard, but the array is
+ * always kept in a consistent enough state to be randomly
+ * accessed without locking by workers performing work-stealing,
+ * as well as other traversal-based methods in this class, so long
+ * as reads memory-acquire by first reading ctl. All readers must
+ * tolerate that some array slots may be null.
+ */
+ ForkJoinWorkerThread[] workers;
+
+ /**
+ * Initial size for submission queue array. Must be a power of
+ * two. In many applications, these always stay small so we use a
+ * small initial cap.
+ */
+ private static final int INITIAL_QUEUE_CAPACITY = 8;
+
+ /**
+ * Maximum size for submission queue array. Must be a power of two
+ * less than or equal to 1 << (31 - width of array entry) to
+ * ensure lack of index wraparound, but is capped at a lower
+ * value to help users trap runaway computations.
+ */
+ private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+
+ /**
+ * Array serving as submission queue. Initialized upon construction.
+ */
+ private ForkJoinTask<?>[] submissionQueue;
+
+ /**
+ * Lock protecting submissions array for addSubmission
+ */
+ private final ReentrantLock submissionLock;
+
+ /**
+ * Condition for awaitTermination, using submissionLock for
+ * convenience.
+ */
+ private final Condition termination;
+
+ /**
+ * Creation factory for worker threads.
+ */
+ private final ForkJoinWorkerThreadFactory factory;
+
+ /**
+ * The uncaught exception handler used when any worker abruptly
+ * terminates.
+ */
+ final Thread.UncaughtExceptionHandler ueh;
+
+ /**
+ * Prefix for assigning names to worker threads
+ */
+ private final String workerNamePrefix;
+
+ /**
+ * Sum of per-thread steal counts, updated only when threads are
+ * idle or terminating.
+ */
+ private volatile long stealCount;
+
+ /**
+ * Main pool control -- a long packed with:
+ * AC: Number of active running workers minus target parallelism (16 bits)
+ * TC: Number of total workers minus target parallelism (16 bits)
+ * ST: true if pool is terminating (1 bit)
+ * EC: the wait count of top waiting thread (15 bits)
+ * ID: ~poolIndex of top of Treiber stack of waiting threads (16 bits)
+ *
+ * When convenient, we can extract the upper 32 bits of counts and
+ * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
+ * (int)ctl. The ec field is never accessed alone, but always
+ * together with id and st. The offsets of counts by the target
+ * parallelism and the positionings of fields makes it possible to
+ * perform the most common checks via sign tests of fields: When
+ * ac is negative, there are not enough active workers, when tc is
+ * negative, there are not enough total workers, when id is
+ * negative, there is at least one waiting worker, and when e is
+ * negative, the pool is terminating. To deal with these possibly
+ * negative fields, we use casts in and out of "short" and/or
+ * signed shifts to maintain signedness.
+ */
+ volatile long ctl;
+
+ // bit positions/shifts for fields
+ private static final int AC_SHIFT = 48;
+ private static final int TC_SHIFT = 32;
+ private static final int ST_SHIFT = 31;
+ private static final int EC_SHIFT = 16;
+
+ // bounds
+ private static final int MAX_ID = 0x7fff; // max poolIndex
+ private static final int SMASK = 0xffff; // mask short bits
+ private static final int SHORT_SIGN = 1 << 15;
+ private static final int INT_SIGN = 1 << 31;
+
+ // masks
+ private static final long STOP_BIT = 0x0001L << ST_SHIFT;
+ private static final long AC_MASK = ((long)SMASK) << AC_SHIFT;
+ private static final long TC_MASK = ((long)SMASK) << TC_SHIFT;
+
+ // units for incrementing and decrementing
+ private static final long TC_UNIT = 1L << TC_SHIFT;
+ private static final long AC_UNIT = 1L << AC_SHIFT;
+
+ // masks and units for dealing with u = (int)(ctl >>> 32)
+ private static final int UAC_SHIFT = AC_SHIFT - 32;
+ private static final int UTC_SHIFT = TC_SHIFT - 32;
+ private static final int UAC_MASK = SMASK << UAC_SHIFT;
+ private static final int UTC_MASK = SMASK << UTC_SHIFT;
+ private static final int UAC_UNIT = 1 << UAC_SHIFT;
+ private static final int UTC_UNIT = 1 << UTC_SHIFT;
+
+ // masks and units for dealing with e = (int)ctl
+ private static final int E_MASK = 0x7fffffff; // no STOP_BIT
+ private static final int EC_UNIT = 1 << EC_SHIFT;
+
+ /**
+ * The target parallelism level.
+ */
+ final int parallelism;
+
+ /**
+ * Index (mod submission queue length) of next element to take
+ * from submission queue. Usage is identical to that for
+ * per-worker queues -- see ForkJoinWorkerThread internal
+ * documentation.
+ */
+ volatile int queueBase;
+
+ /**
+ * Index (mod submission queue length) of next element to add
+ * in submission queue. Usage is identical to that for
+ * per-worker queues -- see ForkJoinWorkerThread internal
+ * documentation.
+ */
+ int queueTop;
+
+ /**
+ * True when shutdown() has been called.
+ */
+ volatile boolean shutdown;
+
+ /**
+ * True if use local fifo, not default lifo, for local polling.
+ * Read by, and replicated by ForkJoinWorkerThreads.
+ */
+ final boolean locallyFifo;
+
+ /**
+ * The number of threads in ForkJoinWorkerThreads.helpQuiescePool.
+ * When non-zero, suppresses automatic shutdown when active
+ * counts become zero.
+ */
+ volatile int quiescerCount;
+
+ /**
+ * The number of threads blocked in join.
+ */
+ volatile int blockedCount;
+
+ /**
+ * Counter for worker Thread names (unrelated to their poolIndex)
+ */
+ private volatile int nextWorkerNumber;
+
+ /**
+ * The index for the next created worker. Accessed under scanGuard.
+ */
+ private int nextWorkerIndex;
+
+ /**
+ * SeqLock and index masking for updates to workers array. Locked
+ * when SG_UNIT is set. Unlocking clears bit by adding
+ * SG_UNIT. Staleness of read-only operations can be checked by
+ * comparing scanGuard to value before the reads. The low 16 bits
+ * (i.e, anding with SMASK) hold (the smallest power of two
+ * covering all worker indices, minus one, and is used to avoid
+ * dealing with large numbers of null slots when the workers array
+ * is overallocated.
+ */
+ volatile int scanGuard;
+
+ private static final int SG_UNIT = 1 << 16;
+
+ /**
+ * The wakeup interval (in nanoseconds) for a worker waiting for a
+ * task when the pool is quiescent to instead try to shrink the
+ * number of workers. The exact value does not matter too
+ * much. It must be short enough to release resources during
+ * sustained periods of idleness, but not so short that threads
+ * are continually re-created.
+ */
+ private static final long SHRINK_RATE =
+ 4L * 1000L * 1000L * 1000L; // 4 seconds
+
+ /**
+ * Top-level loop for worker threads: On each step: if the
+ * previous step swept through all queues and found no tasks, or
+ * there are excess threads, then possibly blocks. Otherwise,
+ * scans for and, if found, executes a task. Returns when pool
+ * and/or worker terminate.
+ *
+ * @param w the worker
+ */
+ final void work(ForkJoinWorkerThread w) {
+ boolean swept = false; // true on empty scans
+ long c;
+ while (!w.terminate && (int)(c = ctl) >= 0) {
+ int a; // active count
+ if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0)
+ swept = scan(w, a);
+ else if (tryAwaitWork(w, c))
+ swept = false;
+ }
+ }
+
+ // Signalling
+
+ /**
+ * Wakes up or creates a worker.
+ */
+ final void signalWork() {
+ /*
+ * The while condition is true if: (there is are too few total
+ * workers OR there is at least one waiter) AND (there are too
+ * few active workers OR the pool is terminating). The value
+ * of e distinguishes the remaining cases: zero (no waiters)
+ * for create, negative if terminating (in which case do
+ * nothing), else release a waiter. The secondary checks for
+ * release (non-null array etc) can fail if the pool begins
+ * terminating after the test, and don't impose any added cost
+ * because JVMs must perform null and bounds checks anyway.
+ */
+ long c; int e, u;
+ while ((((e = (int)(c = ctl)) | (u = (int)(c >>> 32))) &
+ (INT_SIGN|SHORT_SIGN)) == (INT_SIGN|SHORT_SIGN) && e >= 0) {
+ if (e > 0) { // release a waiting worker
+ int i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
+ if ((ws = workers) == null ||
+ (i = ~e & SMASK) >= ws.length ||
+ (w = ws[i]) == null)
+ break;
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (w.eventCount == e &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ break;
+ }
+ }
+ else if (UNSAFE.compareAndSwapLong
+ (this, ctlOffset, c,
+ (long)(((u + UTC_UNIT) & UTC_MASK) |
+ ((u + UAC_UNIT) & UAC_MASK)) << 32)) {
+ addWorker();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Variant of signalWork to help release waiters on rescans.
+ * Tries once to release a waiter if active count < 0.
+ *
+ * @return false if failed due to contention, else true
+ */
+ private boolean tryReleaseWaiter() {
+ long c; int e, i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
+ if ((e = (int)(c = ctl)) > 0 &&
+ (int)(c >> AC_SHIFT) < 0 &&
+ (ws = workers) != null &&
+ (i = ~e & SMASK) < ws.length &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ ((c + AC_UNIT) & (AC_MASK|TC_MASK)));
+ if (w.eventCount != e ||
+ !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
+ return false;
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ }
+ return true;
+ }
+
+ // Scanning for tasks
+
+ /**
+ * Scans for and, if found, executes one task. Scans start at a
+ * random index of workers array, and randomly select the first
+ * (2*#workers)-1 probes, and then, if all empty, resort to 2
+ * circular sweeps, which is necessary to check quiescence. and
+ * taking a submission only if no stealable tasks were found. The
+ * steal code inside the loop is a specialized form of
+ * ForkJoinWorkerThread.deqTask, followed bookkeeping to support
+ * helpJoinTask and signal propagation. The code for submission
+ * queues is almost identical. On each steal, the worker completes
+ * not only the task, but also all local tasks that this task may
+ * have generated. On detecting staleness or contention when
+ * trying to take a task, this method returns without finishing
+ * sweep, which allows global state rechecks before retry.
+ *
+ * @param w the worker
+ * @param a the number of active workers
+ * @return true if swept all queues without finding a task
+ */
+ private boolean scan(ForkJoinWorkerThread w, int a) {
+ int g = scanGuard; // mask 0 avoids useless scans if only one active
+ int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws == null || ws.length <= m) // staleness check
+ return false;
+ for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ ForkJoinWorkerThread v = ws[k & m];
+ if (v != null && (b = v.queueBase) != v.queueTop &&
+ (q = v.queue) != null && (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null && v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ int d = (v.queueBase = b + 1) - v.queueTop;
+ v.stealHint = w.poolIndex;
+ if (d != 0)
+ signalWork(); // propagate if nonempty
+ w.execTask(t);
+ }
+ r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);
+ return false; // store next seed
+ }
+ else if (j < 0) { // xorshift
+ r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;
+ }
+ else
+ ++k;
+ }
+ if (scanGuard != g) // staleness check
+ return false;
+ else { // try to take submission
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ if ((b = queueBase) != queueTop &&
+ (q = submissionQueue) != null &&
+ (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null && queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueBase = b + 1;
+ w.execTask(t);
+ }
+ return false;
+ }
+ return true; // all queues empty
+ }
+ }
+
+ /**
+ * Tries to enqueue worker w in wait queue and await change in
+ * worker's eventCount. If the pool is quiescent and there is
+ * more than one worker, possibly terminates worker upon exit.
+ * Otherwise, before blocking, rescans queues to avoid missed
+ * signals. Upon finding work, releases at least one worker
+ * (which may be the current worker). Rescans restart upon
+ * detected staleness or failure to release due to
+ * contention. Note the unusual conventions about Thread.interrupt
+ * here and elsewhere: Because interrupts are used solely to alert
+ * threads to check termination, which is checked here anyway, we
+ * clear status (using Thread.interrupted) before any call to
+ * park, so that park does not immediately return due to status
+ * being set via some other unrelated call to interrupt in user
+ * code.
+ *
+ * @param w the calling worker
+ * @param c the ctl value on entry
+ * @return true if waited or another thread was released upon enq
+ */
+ private boolean tryAwaitWork(ForkJoinWorkerThread w, long c) {
+ int v = w.eventCount;
+ w.nextWait = (int)c; // w's successor record
+ long nc = (long)(v & E_MASK) | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
+ if (ctl != c || !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ long d = ctl; // return true if lost to a deq, to force scan
+ return (int)d != (int)c && (d & AC_MASK) >= (c & AC_MASK);
+ }
+ for (int sc = w.stealCount; sc != 0;) { // accumulate stealCount
+ long s = stealCount;
+ if (UNSAFE.compareAndSwapLong(this, stealCountOffset, s, s + sc))
+ sc = w.stealCount = 0;
+ else if (w.eventCount != v)
+ return true; // update next time
+ }
+ if ((!shutdown || !tryTerminate(false)) &&
+ (int)c != 0 && parallelism + (int)(nc >> AC_SHIFT) == 0 &&
+ blockedCount == 0 && quiescerCount == 0)
+ idleAwaitWork(w, nc, c, v); // quiescent
+ for (boolean rescanned = false;;) {
+ if (w.eventCount != v)
+ return true;
+ if (!rescanned) {
+ int g = scanGuard, m = g & SMASK;
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null && m < ws.length) {
+ rescanned = true;
+ for (int i = 0; i <= m; ++i) {
+ ForkJoinWorkerThread u = ws[i];
+ if (u != null) {
+ if (u.queueBase != u.queueTop &&
+ !tryReleaseWaiter())
+ rescanned = false; // contended
+ if (w.eventCount != v)
+ return true;
+ }
+ }
+ }
+ if (scanGuard != g || // stale
+ (queueBase != queueTop && !tryReleaseWaiter()))
+ rescanned = false;
+ if (!rescanned)
+ Thread.yield(); // reduce contention
+ else
+ Thread.interrupted(); // clear before park
+ }
+ else {
+ w.parked = true; // must recheck
+ if (w.eventCount != v) {
+ w.parked = false;
+ return true;
+ }
+ LockSupport.park(this);
+ rescanned = w.parked = false;
+ }
+ }
+ }
+
+ /**
+ * If inactivating worker w has caused pool to become
+ * quiescent, check for pool termination, and wait for event
+ * for up to SHRINK_RATE nanosecs (rescans are unnecessary in
+ * this case because quiescence reflects consensus about lack
+ * of work). On timeout, if ctl has not changed, terminate the
+ * worker. Upon its termination (see deregisterWorker), it may
+ * wake up another worker to possibly repeat this process.
+ *
+ * @param w the calling worker
+ * @param currentCtl the ctl value after enqueuing w
+ * @param prevCtl the ctl value if w terminated
+ * @param v the eventCount w awaits change
+ */
+ private void idleAwaitWork(ForkJoinWorkerThread w, long currentCtl,
+ long prevCtl, int v) {
+ if (w.eventCount == v) {
+ if (shutdown)
+ tryTerminate(false);
+ ForkJoinTask.helpExpungeStaleExceptions(); // help clean weak refs
+ while (ctl == currentCtl) {
+ long startTime = System.nanoTime();
+ w.parked = true;
+ if (w.eventCount == v) // must recheck
+ LockSupport.parkNanos(this, SHRINK_RATE);
+ w.parked = false;
+ if (w.eventCount != v)
+ break;
+ else if (System.nanoTime() - startTime <
+ SHRINK_RATE - (SHRINK_RATE / 10)) // timing slop
+ Thread.interrupted(); // spurious wakeup
+ else if (UNSAFE.compareAndSwapLong(this, ctlOffset,
+ currentCtl, prevCtl)) {
+ w.terminate = true; // restore previous
+ w.eventCount = ((int)currentCtl + EC_UNIT) & E_MASK;
+ break;
+ }
+ }
+ }
+ }
+
+ // Submissions
+
+ /**
+ * Enqueues the given task in the submissionQueue. Same idea as
+ * ForkJoinWorkerThread.pushTask except for use of submissionLock.
+ *
+ * @param t the task
+ */
+ private void addSubmission(ForkJoinTask<?> t) {
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ ForkJoinTask<?>[] q; int s, m;
+ if ((q = submissionQueue) != null) { // ignore if queue removed
+ long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;
+ UNSAFE.putOrderedObject(q, u, t);
+ queueTop = s + 1;
+ if (s - queueBase == m)
+ growSubmissionQueue();
+ }
+ } finally {
+ lock.unlock();
+ }
+ signalWork();
+ }
+
+ // (pollSubmission is defined below with exported methods)
+
+ /**
+ * Creates or doubles submissionQueue array.
+ * Basically identical to ForkJoinWorkerThread version.
+ */
+ private void growSubmissionQueue() {
+ ForkJoinTask<?>[] oldQ = submissionQueue;
+ int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ if (size < INITIAL_QUEUE_CAPACITY)
+ size = INITIAL_QUEUE_CAPACITY;
+ ForkJoinTask<?>[] q = submissionQueue = new ForkJoinTask<?>[size];
+ int mask = size - 1;
+ int top = queueTop;
+ int oldMask;
+ if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
+ for (int b = queueBase; b != top; ++b) {
+ long u = ((b & oldMask) << ASHIFT) + ABASE;
+ Object x = UNSAFE.getObjectVolatile(oldQ, u);
+ if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
+ UNSAFE.putObjectVolatile
+ (q, ((b & mask) << ASHIFT) + ABASE, x);
+ }
+ }
+ }
+
+ // Blocking support
+
+ /**
+ * Tries to increment blockedCount, decrement active count
+ * (sometimes implicitly) and possibly release or create a
+ * compensating worker in preparation for blocking. Fails
+ * on contention or termination.
+ *
+ * @return true if the caller can block, else should recheck and retry
+ */
+ private boolean tryPreBlock() {
+ int b = blockedCount;
+ if (UNSAFE.compareAndSwapInt(this, blockedCountOffset, b, b + 1)) {
+ int pc = parallelism;
+ do {
+ ForkJoinWorkerThread[] ws; ForkJoinWorkerThread w;
+ int e, ac, tc, i;
+ long c = ctl;
+ int u = (int)(c >>> 32);
+ if ((e = (int)c) < 0) {
+ // skip -- terminating
+ }
+ else if ((ac = (u >> UAC_SHIFT)) <= 0 && e != 0 &&
+ (ws = workers) != null &&
+ (i = ~e & SMASK) < ws.length &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ (c & (AC_MASK|TC_MASK)));
+ if (w.eventCount == e &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ return true; // release an idle worker
+ }
+ }
+ else if ((tc = (short)(u >>> UTC_SHIFT)) >= 0 && ac + pc > 1) {
+ long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
+ return true; // no compensation needed
+ }
+ else if (tc + pc < MAX_ID) {
+ long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ addWorker();
+ return true; // create a replacement
+ }
+ }
+ // try to back out on any failure and let caller retry
+ } while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
+ b = blockedCount, b - 1));
+ }
+ return false;
+ }
+
+ /**
+ * Decrements blockedCount and increments active count.
+ */
+ private void postBlock() {
+ long c;
+ do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, // no mask
+ c = ctl, c + AC_UNIT));
+ int b;
+ do {} while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
+ b = blockedCount, b - 1));
+ }
+
+ /**
+ * Possibly blocks waiting for the given task to complete, or
+ * cancels the task if terminating. Fails to wait if contended.
+ *
+ * @param joinMe the task
+ */
+ final void tryAwaitJoin(ForkJoinTask<?> joinMe) {
+ Thread.interrupted(); // clear interrupts before checking termination
+ if (joinMe.status >= 0) {
+ if (tryPreBlock()) {
+ joinMe.tryAwaitDone(0L);
+ postBlock();
+ }
+ else if ((ctl & STOP_BIT) != 0L)
+ joinMe.cancelIgnoringExceptions();
+ }
+ }
+
+ /**
+ * Possibly blocks the given worker waiting for joinMe to
+ * complete or timeout.
+ *
+ * @param joinMe the task
+ * @param nanos the wait time for underlying Object.wait
+ */
+ final void timedAwaitJoin(ForkJoinTask<?> joinMe, long nanos) {
+ while (joinMe.status >= 0) {
+ Thread.interrupted();
+ if ((ctl & STOP_BIT) != 0L) {
+ joinMe.cancelIgnoringExceptions();
+ break;
+ }
+ if (tryPreBlock()) {
+ long last = System.nanoTime();
+ while (joinMe.status >= 0) {
+ long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
+ if (millis <= 0)
+ break;
+ joinMe.tryAwaitDone(millis);
+ if (joinMe.status < 0)
+ break;
+ if ((ctl & STOP_BIT) != 0L) {
+ joinMe.cancelIgnoringExceptions();
+ break;
+ }
+ long now = System.nanoTime();
+ nanos -= now - last;
+ last = now;
+ }
+ postBlock();
+ break;
+ }
+ }
+ }
+
+ /**
+ * If necessary, compensates for blocker, and blocks.
+ */
+ private void awaitBlocker(ManagedBlocker blocker)
+ throws InterruptedException {
+ while (!blocker.isReleasable()) {
+ if (tryPreBlock()) {
+ try {
+ do {} while (!blocker.isReleasable() && !blocker.block());
+ } finally {
+ postBlock();
+ }
+ break;
+ }
+ }
+ }
+
+ // Creating, registering and deregistring workers
+
+ /**
+ * Tries to create and start a worker; minimally rolls back counts
+ * on failure.
+ */
+ private void addWorker() {
+ Throwable ex = null;
+ ForkJoinWorkerThread t = null;
+ try {
+ t = factory.newThread(this);
+ } catch (Throwable e) {
+ ex = e;
+ }
+ if (t == null) { // null or exceptional factory return
+ long c; // adjust counts
+ do {} while (!UNSAFE.compareAndSwapLong
+ (this, ctlOffset, c = ctl,
+ (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))));
+ // Propagate exception if originating from an external caller
+ if (!tryTerminate(false) && ex != null &&
+ !(Thread.currentThread() instanceof ForkJoinWorkerThread))
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ }
+ else
+ t.start();
+ }
+
+ /**
+ * Callback from ForkJoinWorkerThread constructor to assign a
+ * public name
+ */
+ final String nextWorkerName() {
+ for (int n;;) {
+ if (UNSAFE.compareAndSwapInt(this, nextWorkerNumberOffset,
+ n = nextWorkerNumber, ++n))
+ return workerNamePrefix + n;
+ }
+ }
+
+ /**
+ * Callback from ForkJoinWorkerThread constructor to
+ * determine its poolIndex and record in workers array.
+ *
+ * @param w the worker
+ * @return the worker's pool index
+ */
+ final int registerWorker(ForkJoinWorkerThread w) {
+ /*
+ * In the typical case, a new worker acquires the lock, uses
+ * next available index and returns quickly. Since we should
+ * not block callers (ultimately from signalWork or
+ * tryPreBlock) waiting for the lock needed to do this, we
+ * instead help release other workers while waiting for the
+ * lock.
+ */
+ for (int g;;) {
+ ForkJoinWorkerThread[] ws;
+ if (((g = scanGuard) & SG_UNIT) == 0 &&
+ UNSAFE.compareAndSwapInt(this, scanGuardOffset,
+ g, g | SG_UNIT)) {
+ int k = nextWorkerIndex;
+ try {
+ if ((ws = workers) != null) { // ignore on shutdown
+ int n = ws.length;
+ if (k < 0 || k >= n || ws[k] != null) {
+ for (k = 0; k < n && ws[k] != null; ++k)
+ ;
+ if (k == n)
+ ws = workers = Arrays.copyOf(ws, n << 1);
+ }
+ ws[k] = w;
+ nextWorkerIndex = k + 1;
+ int m = g & SMASK;
+ g = (k > m) ? ((m << 1) + 1) & SMASK : g + (SG_UNIT<<1);
+ }
+ } finally {
+ scanGuard = g;
+ }
+ return k;
+ }
+ else if ((ws = workers) != null) { // help release others
+ for (ForkJoinWorkerThread u : ws) {
+ if (u != null && u.queueBase != u.queueTop) {
+ if (tryReleaseWaiter())
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Final callback from terminating worker. Removes record of
+ * worker from array, and adjusts counts. If pool is shutting
+ * down, tries to complete termination.
+ *
+ * @param w the worker
+ */
+ final void deregisterWorker(ForkJoinWorkerThread w, Throwable ex) {
+ int idx = w.poolIndex;
+ int sc = w.stealCount;
+ int steps = 0;
+ // Remove from array, adjust worker counts and collect steal count.
+ // We can intermix failed removes or adjusts with steal updates
+ do {
+ long s, c;
+ int g;
+ if (steps == 0 && ((g = scanGuard) & SG_UNIT) == 0 &&
+ UNSAFE.compareAndSwapInt(this, scanGuardOffset,
+ g, g |= SG_UNIT)) {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null && idx >= 0 &&
+ idx < ws.length && ws[idx] == w)
+ ws[idx] = null; // verify
+ nextWorkerIndex = idx;
+ scanGuard = g + SG_UNIT;
+ steps = 1;
+ }
+ if (steps == 1 &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
+ (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))))
+ steps = 2;
+ if (sc != 0 &&
+ UNSAFE.compareAndSwapLong(this, stealCountOffset,
+ s = stealCount, s + sc))
+ sc = 0;
+ } while (steps != 2 || sc != 0);
+ if (!tryTerminate(false)) {
+ if (ex != null) // possibly replace if died abnormally
+ signalWork();
+ else
+ tryReleaseWaiter();
+ }
+ }
+
+ // Shutdown and termination
+
+ /**
+ * Possibly initiates and/or completes termination.
+ *
+ * @param now if true, unconditionally terminate, else only
+ * if shutdown and empty queue and no active workers
+ * @return true if now terminating or terminated
+ */
+ private boolean tryTerminate(boolean now) {
+ long c;
+ while (((c = ctl) & STOP_BIT) == 0) {
+ if (!now) {
+ if ((int)(c >> AC_SHIFT) != -parallelism)
+ return false;
+ if (!shutdown || blockedCount != 0 || quiescerCount != 0 ||
+ queueBase != queueTop) {
+ if (ctl == c) // staleness check
+ return false;
+ continue;
+ }
+ }
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, c | STOP_BIT))
+ startTerminating();
+ }
+ if ((short)(c >>> TC_SHIFT) == -parallelism) { // signal when 0 workers
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ termination.signalAll();
+ } finally {
+ lock.unlock();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Runs up to three passes through workers: (0) Setting
+ * termination status for each worker, followed by wakeups up to
+ * queued workers; (1) helping cancel tasks; (2) interrupting
+ * lagging threads (likely in external tasks, but possibly also
+ * blocked in joins). Each pass repeats previous steps because of
+ * potential lagging thread creation.
+ */
+ private void startTerminating() {
+ cancelSubmissions();
+ for (int pass = 0; pass < 3; ++pass) {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null) {
+ for (ForkJoinWorkerThread w : ws) {
+ if (w != null) {
+ w.terminate = true;
+ if (pass > 0) {
+ w.cancelTasks();
+ if (pass > 1 && !w.isInterrupted()) {
+ try {
+ w.interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ }
+ }
+ }
+ terminateWaiters();
+ }
+ }
+ }
+
+ /**
+ * Polls and cancels all submissions. Called only during termination.
+ */
+ private void cancelSubmissions() {
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> task = pollSubmission();
+ if (task != null) {
+ try {
+ task.cancel(false);
+ } catch (Throwable ignore) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to set the termination status of waiting workers, and
+ * then wakes them up (after which they will terminate).
+ */
+ private void terminateWaiters() {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null) {
+ ForkJoinWorkerThread w; long c; int i, e;
+ int n = ws.length;
+ while ((i = ~(e = (int)(c = ctl)) & SMASK) < n &&
+ (w = ws[i]) != null && w.eventCount == (e & E_MASK)) {
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c,
+ (long)(w.nextWait & E_MASK) |
+ ((c + AC_UNIT) & AC_MASK) |
+ (c & (TC_MASK|STOP_BIT)))) {
+ w.terminate = true;
+ w.eventCount = e + EC_UNIT;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ }
+ }
+ }
+ }
+
+ // misc ForkJoinWorkerThread support
+
+ /**
+ * Increments or decrements quiescerCount. Needed only to prevent
+ * triggering shutdown if a worker is transiently inactive while
+ * checking quiescence.
+ *
+ * @param delta 1 for increment, -1 for decrement
+ */
+ final void addQuiescerCount(int delta) {
+ int c;
+ do {} while (!UNSAFE.compareAndSwapInt(this, quiescerCountOffset,
+ c = quiescerCount, c + delta));
+ }
+
+ /**
+ * Directly increments or decrements active count without queuing.
+ * This method is used to transiently assert inactivation while
+ * checking quiescence.
+ *
+ * @param delta 1 for increment, -1 for decrement
+ */
+ final void addActiveCount(int delta) {
+ long d = (long)delta << AC_SHIFT;
+ long c;
+ do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset,
+ c = ctl, c + d));
+ }
+
+ /**
+ * Returns the approximate (non-atomic) number of idle threads per
+ * active thread.
+ */
+ final int idlePerActive() {
+ // Approximate at powers of two for small values, saturate past 4
+ int p = parallelism;
+ int a = p + (int)(ctl >> AC_SHIFT);
+ return (a > (p >>>= 1) ? 0 :
+ a > (p >>>= 1) ? 1 :
+ a > (p >>>= 1) ? 2 :
+ a > (p >>>= 1) ? 4 :
+ 8);
+ }
+
+ // Exported methods
+
+ // Constructors
+
+ /**
+ * Creates a {@code ForkJoinPool} with parallelism equal to {@link
+ * java.lang.Runtime#availableProcessors}, using the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ */
+ public ForkJoinPool() {
+ this(Runtime.getRuntime().availableProcessors(),
+ defaultForkJoinWorkerThreadFactory, null, false);
+ }
+
+ /**
+ * Creates a {@code ForkJoinPool} with the indicated parallelism
+ * level, the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ *
+ * @param parallelism the parallelism level
+ * @throws IllegalArgumentException if parallelism less than or
+ * equal to zero, or greater than implementation limit
+ */
+ public ForkJoinPool(int parallelism) {
+ this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
+ }
+
+ /**
+ * Creates a {@code ForkJoinPool} with the given parameters.
+ *
+ * @param parallelism the parallelism level. For default value,
+ * use {@link java.lang.Runtime#availableProcessors}.
+ * @param factory the factory for creating new threads. For default value,
+ * use {@link #defaultForkJoinWorkerThreadFactory}.
+ * @param handler the handler for internal worker threads that
+ * terminate due to unrecoverable errors encountered while executing
+ * tasks. For default value, use {@code null}.
+ * @param asyncMode if true,
+ * establishes local first-in-first-out scheduling mode for forked
+ * tasks that are never joined. This mode may be more appropriate
+ * than default locally stack-based mode in applications in which
+ * worker threads only process event-style asynchronous tasks.
+ * For default value, use {@code false}.
+ * @throws IllegalArgumentException if parallelism less than or
+ * equal to zero, or greater than implementation limit
+ * @throws NullPointerException if the factory is null
+ */
+ public ForkJoinPool(int parallelism,
+ ForkJoinWorkerThreadFactory factory,
+ Thread.UncaughtExceptionHandler handler,
+ boolean asyncMode) {
+ checkPermission();
+ if (factory == null)
+ throw new NullPointerException();
+ if (parallelism <= 0 || parallelism > MAX_ID)
+ throw new IllegalArgumentException();
+ this.parallelism = parallelism;
+ this.factory = factory;
+ this.ueh = handler;
+ this.locallyFifo = asyncMode;
+ long np = (long)(-parallelism); // offset ctl counts
+ this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+ this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
+ // initialize workers array with room for 2*parallelism if possible
+ int n = parallelism << 1;
+ if (n >= MAX_ID)
+ n = MAX_ID;
+ else { // See Hackers Delight, sec 3.2, where n < (1 << 16)
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8;
+ }
+ workers = new ForkJoinWorkerThread[n + 1];
+ this.submissionLock = new ReentrantLock();
+ this.termination = submissionLock.newCondition();
+ StringBuilder sb = new StringBuilder("ForkJoinPool-");
+ sb.append(poolNumberGenerator.incrementAndGet());
+ sb.append("-worker-");
+ this.workerNamePrefix = sb.toString();
+ }
+
+ // Execution methods
+
+ /**
+ * Performs the given task, returning its result upon completion.
+ * If the computation encounters an unchecked Exception or Error,
+ * it is rethrown as the outcome of this invocation. Rethrown
+ * exceptions behave in the same way as regular exceptions, but,
+ * when possible, contain stack traces (as displayed for example
+ * using {@code ex.printStackTrace()}) of both the current thread
+ * as well as the thread actually encountering the exception;
+ * minimally only the latter.
+ *
+ * @param task the task
+ * @return the task's result
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> T invoke(ForkJoinTask<T> task) {
+ Thread t = Thread.currentThread();
+ if (task == null)
+ throw new NullPointerException();
+ if (shutdown)
+ throw new RejectedExecutionException();
+ if ((t instanceof ForkJoinWorkerThread) &&
+ ((ForkJoinWorkerThread)t).pool == this)
+ return task.invoke(); // bypass submit if in same pool
+ else {
+ addSubmission(task);
+ return task.join();
+ }
+ }
+
+ /**
+ * Unless terminating, forks task if within an ongoing FJ
+ * computation in the current pool, else submits as external task.
+ */
+ private <T> void forkOrSubmit(ForkJoinTask<T> task) {
+ ForkJoinWorkerThread w;
+ Thread t = Thread.currentThread();
+ if (shutdown)
+ throw new RejectedExecutionException();
+ if ((t instanceof ForkJoinWorkerThread) &&
+ (w = (ForkJoinWorkerThread)t).pool == this)
+ w.pushTask(task);
+ else
+ addSubmission(task);
+ }
+
+ /**
+ * Arranges for (asynchronous) execution of the given task.
+ *
+ * @param task the task
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public void execute(ForkJoinTask<?> task) {
+ if (task == null)
+ throw new NullPointerException();
+ forkOrSubmit(task);
+ }
+
+ // AbstractExecutorService methods
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public void execute(Runnable task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<?> job;
+ if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+ job = (ForkJoinTask<?>) task;
+ else
+ job = ForkJoinTask.adapt(task, null);
+ forkOrSubmit(job);
+ }
+
+ /**
+ * Submits a ForkJoinTask for execution.
+ *
+ * @param task the task to submit
+ * @return the task
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
+ if (task == null)
+ throw new NullPointerException();
+ forkOrSubmit(task);
+ return task;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(Callable<T> task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<T> job = ForkJoinTask.adapt(task);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(Runnable task, T result) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<T> job = ForkJoinTask.adapt(task, result);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public ForkJoinTask<?> submit(Runnable task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<?> job;
+ if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+ job = (ForkJoinTask<?>) task;
+ else
+ job = ForkJoinTask.adapt(task, null);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws RejectedExecutionException {@inheritDoc}
+ */
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
+ ArrayList<ForkJoinTask<T>> forkJoinTasks =
+ new ArrayList<ForkJoinTask<T>>(tasks.size());
+ for (Callable<T> task : tasks)
+ forkJoinTasks.add(ForkJoinTask.adapt(task));
+ invoke(new InvokeAll<T>(forkJoinTasks));
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ List<Future<T>> futures = (List<Future<T>>) (List) forkJoinTasks;
+ return futures;
+ }
+
+ static final class InvokeAll<T> extends RecursiveAction {
+ final ArrayList<ForkJoinTask<T>> tasks;
+ InvokeAll(ArrayList<ForkJoinTask<T>> tasks) { this.tasks = tasks; }
+ public void compute() {
+ try { invokeAll(tasks); }
+ catch (Exception ignore) {}
+ }
+ private static final long serialVersionUID = -7914297376763021607L;
+ }
+
+ /**
+ * Returns the factory used for constructing new workers.
+ *
+ * @return the factory used for constructing new workers
+ */
+ public ForkJoinWorkerThreadFactory getFactory() {
+ return factory;
+ }
+
+ /**
+ * Returns the handler for internal worker threads that terminate
+ * due to unrecoverable errors encountered while executing tasks.
+ *
+ * @return the handler, or {@code null} if none
+ */
+ public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
+ return ueh;
+ }
+
+ /**
+ * Returns the targeted parallelism level of this pool.
+ *
+ * @return the targeted parallelism level of this pool
+ */
+ public int getParallelism() {
+ return parallelism;
+ }
+
+ /**
+ * Returns the number of worker threads that have started but not
+ * yet terminated. The result returned by this method may differ
+ * from {@link #getParallelism} when threads are created to
+ * maintain parallelism when others are cooperatively blocked.
+ *
+ * @return the number of worker threads
+ */
+ public int getPoolSize() {
+ return parallelism + (short)(ctl >>> TC_SHIFT);
+ }
+
+ /**
+ * Returns {@code true} if this pool uses local first-in-first-out
+ * scheduling mode for forked tasks that are never joined.
+ *
+ * @return {@code true} if this pool uses async mode
+ */
+ public boolean getAsyncMode() {
+ return locallyFifo;
+ }
+
+ /**
+ * Returns an estimate of the number of worker threads that are
+ * not blocked waiting to join tasks or for other managed
+ * synchronization. This method may overestimate the
+ * number of running threads.
+ *
+ * @return the number of worker threads
+ */
+ public int getRunningThreadCount() {
+ int r = parallelism + (int)(ctl >> AC_SHIFT);
+ return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ }
+
+ /**
+ * Returns an estimate of the number of threads that are currently
+ * stealing or executing tasks. This method may overestimate the
+ * number of active threads.
+ *
+ * @return the number of active threads
+ */
+ public int getActiveThreadCount() {
+ int r = parallelism + (int)(ctl >> AC_SHIFT) + blockedCount;
+ return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ }
+
+ /**
+ * Returns {@code true} if all worker threads are currently idle.
+ * An idle worker is one that cannot obtain a task to execute
+ * because none are available to steal from other threads, and
+ * there are no pending submissions to the pool. This method is
+ * conservative; it might not return {@code true} immediately upon
+ * idleness of all threads, but will eventually become true if
+ * threads remain inactive.
+ *
+ * @return {@code true} if all threads are currently idle
+ */
+ public boolean isQuiescent() {
+ return parallelism + (int)(ctl >> AC_SHIFT) + blockedCount == 0;
+ }
+
+ /**
+ * Returns an estimate of the total number of tasks stolen from
+ * one thread's work queue by another. The reported value
+ * underestimates the actual total number of steals when the pool
+ * is not quiescent. This value may be useful for monitoring and
+ * tuning fork/join programs: in general, steal counts should be
+ * high enough to keep threads busy, but low enough to avoid
+ * overhead and contention across threads.
+ *
+ * @return the number of steals
+ */
+ public long getStealCount() {
+ return stealCount;
+ }
+
+ /**
+ * Returns an estimate of the total number of tasks currently held
+ * in queues by worker threads (but not including tasks submitted
+ * to the pool that have not begun executing). This value is only
+ * an approximation, obtained by iterating across all threads in
+ * the pool. This method may be useful for tuning task
+ * granularities.
+ *
+ * @return the number of queued tasks
+ */
+ public long getQueuedTaskCount() {
+ long count = 0;
+ ForkJoinWorkerThread[] ws;
+ if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
+ (ws = workers) != null) {
+ for (ForkJoinWorkerThread w : ws)
+ if (w != null)
+ count -= w.queueBase - w.queueTop; // must read base first
+ }
+ return count;
+ }
+
+ /**
+ * Returns an estimate of the number of tasks submitted to this
+ * pool that have not yet begun executing. This method may take
+ * time proportional to the number of submissions.
+ *
+ * @return the number of queued submissions
+ */
+ public int getQueuedSubmissionCount() {
+ return -queueBase + queueTop;
+ }
+
+ /**
+ * Returns {@code true} if there are any tasks submitted to this
+ * pool that have not yet begun executing.
+ *
+ * @return {@code true} if there are any queued submissions
+ */
+ public boolean hasQueuedSubmissions() {
+ return queueBase != queueTop;
+ }
+
+ /**
+ * Removes and returns the next unexecuted submission if one is
+ * available. This method may be useful in extensions to this
+ * class that re-assign work in systems with multiple pools.
+ *
+ * @return the next submission, or {@code null} if none
+ */
+ protected ForkJoinTask<?> pollSubmission() {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ while ((b = queueBase) != queueTop &&
+ (q = submissionQueue) != null &&
+ (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null &&
+ queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes all available unexecuted submitted and forked tasks
+ * from scheduling queues and adds them to the given collection,
+ * without altering their execution status. These may include
+ * artificially generated or wrapped tasks. This method is
+ * designed to be invoked only when the pool is known to be
+ * quiescent. Invocations at other times may not remove all
+ * tasks. A failure encountered while attempting to add elements
+ * to collection {@code c} may result in elements being in
+ * neither, either or both collections when the associated
+ * exception is thrown. The behavior of this operation is
+ * undefined if the specified collection is modified while the
+ * operation is in progress.
+ *
+ * @param c the collection to transfer elements into
+ * @return the number of elements transferred
+ */
+ protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+ int count = 0;
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = pollSubmission();
+ if (t != null) {
+ c.add(t);
+ ++count;
+ }
+ }
+ ForkJoinWorkerThread[] ws;
+ if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
+ (ws = workers) != null) {
+ for (ForkJoinWorkerThread w : ws)
+ if (w != null)
+ count += w.drainTasksTo(c);
+ }
+ return count;
+ }
+
+ /**
+ * Returns a string identifying this pool, as well as its state,
+ * including indications of run state, parallelism level, and
+ * worker and task counts.
+ *
+ * @return a string identifying this pool, as well as its state
+ */
+ public String toString() {
+ long st = getStealCount();
+ long qt = getQueuedTaskCount();
+ long qs = getQueuedSubmissionCount();
+ int pc = parallelism;
+ long c = ctl;
+ int tc = pc + (short)(c >>> TC_SHIFT);
+ int rc = pc + (int)(c >> AC_SHIFT);
+ if (rc < 0) // ignore transient negative
+ rc = 0;
+ int ac = rc + blockedCount;
+ String level;
+ if ((c & STOP_BIT) != 0)
+ level = (tc == 0) ? "Terminated" : "Terminating";
+ else
+ level = shutdown ? "Shutting down" : "Running";
+ return super.toString() +
+ "[" + level +
+ ", parallelism = " + pc +
+ ", size = " + tc +
+ ", active = " + ac +
+ ", running = " + rc +
+ ", steals = " + st +
+ ", tasks = " + qt +
+ ", submissions = " + qs +
+ "]";
+ }
+
+ /**
+ * Initiates an orderly shutdown in which previously submitted
+ * tasks are executed, but no new tasks will be accepted.
+ * Invocation has no additional effect if already shut down.
+ * Tasks that are in the process of being submitted concurrently
+ * during the course of this method may or may not be rejected.
+ */
+ public void shutdown() {
+ checkPermission();
+ shutdown = true;
+ tryTerminate(false);
+ }
+
+ /**
+ * Attempts to cancel and/or stop all tasks, and reject all
+ * subsequently submitted tasks. Tasks that are in the process of
+ * being submitted or executed concurrently during the course of
+ * this method may or may not be rejected. This method cancels
+ * both existing and unexecuted tasks, in order to permit
+ * termination in the presence of task dependencies. So the method
+ * always returns an empty list (unlike the case for some other
+ * Executors).
+ *
+ * @return an empty list
+ */
+ public List<Runnable> shutdownNow() {
+ checkPermission();
+ shutdown = true;
+ tryTerminate(true);
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns {@code true} if all tasks have completed following shut down.
+ *
+ * @return {@code true} if all tasks have completed following shut down
+ */
+ public boolean isTerminated() {
+ long c = ctl;
+ return ((c & STOP_BIT) != 0L &&
+ (short)(c >>> TC_SHIFT) == -parallelism);
+ }
+
+ /**
+ * Returns {@code true} if the process of termination has
+ * commenced but not yet completed. This method may be useful for
+ * debugging. A return of {@code true} reported a sufficient
+ * period after shutdown may indicate that submitted tasks have
+ * ignored or suppressed interruption, or are waiting for IO,
+ * causing this executor not to properly terminate. (See the
+ * advisory notes for class {@link ForkJoinTask} stating that
+ * tasks should not normally entail blocking operations. But if
+ * they do, they must abort them on interrupt.)
+ *
+ * @return {@code true} if terminating but not yet terminated
+ */
+ public boolean isTerminating() {
+ long c = ctl;
+ return ((c & STOP_BIT) != 0L &&
+ (short)(c >>> TC_SHIFT) != -parallelism);
+ }
+
+ /**
+ * Returns true if terminating or terminated. Used by ForkJoinWorkerThread.
+ */
+ final boolean isAtLeastTerminating() {
+ return (ctl & STOP_BIT) != 0L;
+ }
+
+ /**
+ * Returns {@code true} if this pool has been shut down.
+ *
+ * @return {@code true} if this pool has been shut down
+ */
+ public boolean isShutdown() {
+ return shutdown;
+ }
+
+ /**
+ * Blocks until all tasks have completed execution after a shutdown
+ * request, or the timeout occurs, or the current thread is
+ * interrupted, whichever happens first.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return {@code true} if this executor terminated and
+ * {@code false} if the timeout elapsed before termination
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean awaitTermination(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ long nanos = unit.toNanos(timeout);
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ for (;;) {
+ if (isTerminated())
+ return true;
+ if (nanos <= 0)
+ return false;
+ nanos = termination.awaitNanos(nanos);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Interface for extending managed parallelism for tasks running
+ * in {@link ForkJoinPool}s.
+ *
+ * <p>A {@code ManagedBlocker} provides two methods. Method
+ * {@code isReleasable} must return {@code true} if blocking is
+ * not necessary. Method {@code block} blocks the current thread
+ * if necessary (perhaps internally invoking {@code isReleasable}
+ * before actually blocking). These actions are performed by any
+ * thread invoking {@link ForkJoinPool#managedBlock}. The
+ * unusual methods in this API accommodate synchronizers that may,
+ * but don't usually, block for long periods. Similarly, they
+ * allow more efficient internal handling of cases in which
+ * additional workers may be, but usually are not, needed to
+ * ensure sufficient parallelism. Toward this end,
+ * implementations of method {@code isReleasable} must be amenable
+ * to repeated invocation.
+ *
+ * <p>For example, here is a ManagedBlocker based on a
+ * ReentrantLock:
+ * <pre> {@code
+ * class ManagedLocker implements ManagedBlocker {
+ * final ReentrantLock lock;
+ * boolean hasLock = false;
+ * ManagedLocker(ReentrantLock lock) { this.lock = lock; }
+ * public boolean block() {
+ * if (!hasLock)
+ * lock.lock();
+ * return true;
+ * }
+ * public boolean isReleasable() {
+ * return hasLock || (hasLock = lock.tryLock());
+ * }
+ * }}</pre>
+ *
+ * <p>Here is a class that possibly blocks waiting for an
+ * item on a given queue:
+ * <pre> {@code
+ * class QueueTaker<E> implements ManagedBlocker {
+ * final BlockingQueue<E> queue;
+ * volatile E item = null;
+ * QueueTaker(BlockingQueue<E> q) { this.queue = q; }
+ * public boolean block() throws InterruptedException {
+ * if (item == null)
+ * item = queue.take();
+ * return true;
+ * }
+ * public boolean isReleasable() {
+ * return item != null || (item = queue.poll()) != null;
+ * }
+ * public E getItem() { // call after pool.managedBlock completes
+ * return item;
+ * }
+ * }}</pre>
+ */
+ public static interface ManagedBlocker {
+ /**
+ * Possibly blocks the current thread, for example waiting for
+ * a lock or condition.
+ *
+ * @return {@code true} if no additional blocking is necessary
+ * (i.e., if isReleasable would return true)
+ * @throws InterruptedException if interrupted while waiting
+ * (the method is not required to do so, but is allowed to)
+ */
+ boolean block() throws InterruptedException;
+
+ /**
+ * Returns {@code true} if blocking is unnecessary.
+ */
+ boolean isReleasable();
+ }
+
+ /**
+ * Blocks in accord with the given blocker. If the current thread
+ * is a {@link ForkJoinWorkerThread}, this method possibly
+ * arranges for a spare thread to be activated if necessary to
+ * ensure sufficient parallelism while the current thread is blocked.
+ *
+ * <p>If the caller is not a {@link ForkJoinTask}, this method is
+ * behaviorally equivalent to
+ * <pre> {@code
+ * while (!blocker.isReleasable())
+ * if (blocker.block())
+ * return;
+ * }</pre>
+ *
+ * If the caller is a {@code ForkJoinTask}, then the pool may
+ * first be expanded to ensure parallelism, and later adjusted.
+ *
+ * @param blocker the blocker
+ * @throws InterruptedException if blocker.block did so
+ */
+ public static void managedBlock(ManagedBlocker blocker)
+ throws InterruptedException {
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
+ w.pool.awaitBlocker(blocker);
+ }
+ else {
+ do {} while (!blocker.isReleasable() && !blocker.block());
+ }
+ }
+
+ // AbstractExecutorService overrides. These rely on undocumented
+ // fact that ForkJoinTask.adapt returns ForkJoinTasks that also
+ // implement RunnableFuture.
+
+ protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
+ return (RunnableFuture<T>) ForkJoinTask.adapt(runnable, value);
+ }
+
+ protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
+ return (RunnableFuture<T>) ForkJoinTask.adapt(callable);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long ctlOffset;
+ private static final long stealCountOffset;
+ private static final long blockedCountOffset;
+ private static final long quiescerCountOffset;
+ private static final long scanGuardOffset;
+ private static final long nextWorkerNumberOffset;
+ private static final long ABASE;
+ private static final int ASHIFT;
+
+ static {
+ poolNumberGenerator = new AtomicInteger();
+ workerSeedGenerator = new Random();
+ modifyThreadPermission = new RuntimePermission("modifyThread");
+ defaultForkJoinWorkerThreadFactory =
+ new DefaultForkJoinWorkerThreadFactory();
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ForkJoinPool.class;
+ ctlOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("ctl"));
+ stealCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("stealCount"));
+ blockedCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("blockedCount"));
+ quiescerCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("quiescerCount"));
+ scanGuardOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("scanGuard"));
+ nextWorkerNumberOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("nextWorkerNumber"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ Class<?> a = ForkJoinTask[].class;
+ ABASE = UNSAFE.arrayBaseOffset(a);
+ int s = UNSAFE.arrayIndexScale(a);
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
new file mode 100644
index 0000000..86a29d7
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -0,0 +1,1357 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantLock;
+import java.lang.reflect.Constructor;
+import libcore.util.SneakyThrow;
+
+/**
+ * Abstract base class for tasks that run within a {@link ForkJoinPool}.
+ * A {@code ForkJoinTask} is a thread-like entity that is much
+ * lighter weight than a normal thread. Huge numbers of tasks and
+ * subtasks may be hosted by a small number of actual threads in a
+ * ForkJoinPool, at the price of some usage limitations.
+ *
+ * <p>A "main" {@code ForkJoinTask} begins execution when submitted
+ * to a {@link ForkJoinPool}. Once started, it will usually in turn
+ * start other subtasks. As indicated by the name of this class,
+ * many programs using {@code ForkJoinTask} employ only methods
+ * {@link #fork} and {@link #join}, or derivatives such as {@link
+ * #invokeAll(ForkJoinTask...) invokeAll}. However, this class also
+ * provides a number of other methods that can come into play in
+ * advanced usages, as well as extension mechanics that allow
+ * support of new forms of fork/join processing.
+ *
+ * <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
+ * The efficiency of {@code ForkJoinTask}s stems from a set of
+ * restrictions (that are only partially statically enforceable)
+ * reflecting their intended use as computational tasks calculating
+ * pure functions or operating on purely isolated objects. The
+ * primary coordination mechanisms are {@link #fork}, that arranges
+ * asynchronous execution, and {@link #join}, that doesn't proceed
+ * until the task's result has been computed. Computations should
+ * avoid {@code synchronized} methods or blocks, and should minimize
+ * other blocking synchronization apart from joining other tasks or
+ * using synchronizers such as Phasers that are advertised to
+ * cooperate with fork/join scheduling. Tasks should also not perform
+ * blocking IO, and should ideally access variables that are
+ * completely independent of those accessed by other running
+ * tasks. Minor breaches of these restrictions, for example using
+ * shared output streams, may be tolerable in practice, but frequent
+ * use may result in poor performance, and the potential to
+ * indefinitely stall if the number of threads not waiting for IO or
+ * other external synchronization becomes exhausted. This usage
+ * restriction is in part enforced by not permitting checked
+ * exceptions such as {@code IOExceptions} to be thrown. However,
+ * computations may still encounter unchecked exceptions, that are
+ * rethrown to callers attempting to join them. These exceptions may
+ * additionally include {@link RejectedExecutionException} stemming
+ * from internal resource exhaustion, such as failure to allocate
+ * internal task queues. Rethrown exceptions behave in the same way as
+ * regular exceptions, but, when possible, contain stack traces (as
+ * displayed for example using {@code ex.printStackTrace()}) of both
+ * the thread that initiated the computation as well as the thread
+ * actually encountering the exception; minimally only the latter.
+ *
+ * <p>The primary method for awaiting completion and extracting
+ * results of a task is {@link #join}, but there are several variants:
+ * The {@link Future#get} methods support interruptible and/or timed
+ * waits for completion and report results using {@code Future}
+ * conventions. Method {@link #invoke} is semantically
+ * equivalent to {@code fork(); join()} but always attempts to begin
+ * execution in the current thread. The "<em>quiet</em>" forms of
+ * these methods do not extract results or report exceptions. These
+ * may be useful when a set of tasks are being executed, and you need
+ * to delay processing of results or exceptions until all complete.
+ * Method {@code invokeAll} (available in multiple versions)
+ * performs the most common form of parallel invocation: forking a set
+ * of tasks and joining them all.
+ *
+ * <p>The execution status of tasks may be queried at several levels
+ * of detail: {@link #isDone} is true if a task completed in any way
+ * (including the case where a task was cancelled without executing);
+ * {@link #isCompletedNormally} is true if a task completed without
+ * cancellation or encountering an exception; {@link #isCancelled} is
+ * true if the task was cancelled (in which case {@link #getException}
+ * returns a {@link java.util.concurrent.CancellationException}); and
+ * {@link #isCompletedAbnormally} is true if a task was either
+ * cancelled or encountered an exception, in which case {@link
+ * #getException} will return either the encountered exception or
+ * {@link java.util.concurrent.CancellationException}.
+ *
+ * <p>The ForkJoinTask class is not usually directly subclassed.
+ * Instead, you subclass one of the abstract classes that support a
+ * particular style of fork/join processing, typically {@link
+ * RecursiveAction} for computations that do not return results, or
+ * {@link RecursiveTask} for those that do. Normally, a concrete
+ * ForkJoinTask subclass declares fields comprising its parameters,
+ * established in a constructor, and then defines a {@code compute}
+ * method that somehow uses the control methods supplied by this base
+ * class. While these methods have {@code public} access (to allow
+ * instances of different task subclasses to call each other's
+ * methods), some of them may only be called from within other
+ * ForkJoinTasks (as may be determined using method {@link
+ * #inForkJoinPool}). Attempts to invoke them in other contexts
+ * result in exceptions or errors, possibly including
+ * {@code ClassCastException}.
+ *
+ * <p>Method {@link #join} and its variants are appropriate for use
+ * only when completion dependencies are acyclic; that is, the
+ * parallel computation can be described as a directed acyclic graph
+ * (DAG). Otherwise, executions may encounter a form of deadlock as
+ * tasks cyclically wait for each other. However, this framework
+ * supports other methods and techniques (for example the use of
+ * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
+ * may be of use in constructing custom subclasses for problems that
+ * are not statically structured as DAGs.
+ *
+ * <p>Most base support methods are {@code final}, to prevent
+ * overriding of implementations that are intrinsically tied to the
+ * underlying lightweight task scheduling framework. Developers
+ * creating new basic styles of fork/join processing should minimally
+ * implement {@code protected} methods {@link #exec}, {@link
+ * #setRawResult}, and {@link #getRawResult}, while also introducing
+ * an abstract computational method that can be implemented in its
+ * subclasses, possibly relying on other {@code protected} methods
+ * provided by this class.
+ *
+ * <p>ForkJoinTasks should perform relatively small amounts of
+ * computation. Large tasks should be split into smaller subtasks,
+ * usually via recursive decomposition. As a very rough rule of thumb,
+ * a task should perform more than 100 and less than 10000 basic
+ * computational steps, and should avoid indefinite looping. If tasks
+ * are too big, then parallelism cannot improve throughput. If too
+ * small, then memory and internal task maintenance overhead may
+ * overwhelm processing.
+ *
+ * <p>This class provides {@code adapt} methods for {@link Runnable}
+ * and {@link Callable}, that may be of use when mixing execution of
+ * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are
+ * of this form, consider using a pool constructed in <em>asyncMode</em>.
+ *
+ * <p>ForkJoinTasks are {@code Serializable}, which enables them to be
+ * used in extensions such as remote execution frameworks. It is
+ * sensible to serialize tasks only before or after, but not during,
+ * execution. Serialization is not relied on during execution itself.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
+
+ /*
+ * See the internal documentation of class ForkJoinPool for a
+ * general implementation overview. ForkJoinTasks are mainly
+ * responsible for maintaining their "status" field amidst relays
+ * to methods in ForkJoinWorkerThread and ForkJoinPool.
+ *
+ * The methods of this class are more-or-less layered into
+ * (1) basic status maintenance
+ * (2) execution and awaiting completion
+ * (3) user-level methods that additionally report results.
+ * This is sometimes hard to see because this file orders exported
+ * methods in a way that flows well in javadocs.
+ */
+
+ /*
+ * The status field holds run control status bits packed into a
+ * single int to minimize footprint and to ensure atomicity (via
+ * CAS). Status is initially zero, and takes on nonnegative
+ * values until completed, upon which status holds value
+ * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking
+ * waits by other threads have the SIGNAL bit set. Completion of
+ * a stolen task with SIGNAL set awakens any waiters via
+ * notifyAll. Even though suboptimal for some purposes, we use
+ * basic builtin wait/notify to take advantage of "monitor
+ * inflation" in JVMs that we would otherwise need to emulate to
+ * avoid adding further per-task bookkeeping overhead. We want
+ * these monitors to be "fat", i.e., not use biasing or thin-lock
+ * techniques, so use some odd coding idioms that tend to avoid
+ * them.
+ */
+
+ /** The run status of this task */
+ volatile int status; // accessed directly by pool and workers
+ private static final int NORMAL = -1;
+ private static final int CANCELLED = -2;
+ private static final int EXCEPTIONAL = -3;
+ private static final int SIGNAL = 1;
+
+ /**
+ * Marks completion and wakes up threads waiting to join this task,
+ * also clearing signal request bits.
+ *
+ * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
+ * @return completion status on exit
+ */
+ private int setCompletion(int completion) {
+ for (int s;;) {
+ if ((s = status) < 0)
+ return s;
+ if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
+ if (s != 0)
+ synchronized (this) { notifyAll(); }
+ return completion;
+ }
+ }
+ }
+
+ /**
+ * Tries to block a worker thread until completed or timed out.
+ * Uses Object.wait time argument conventions.
+ * May fail on contention or interrupt.
+ *
+ * @param millis if > 0, wait time.
+ */
+ final void tryAwaitDone(long millis) {
+ int s;
+ try {
+ if (((s = status) > 0 ||
+ (s == 0 &&
+ UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL))) &&
+ status > 0) {
+ synchronized (this) {
+ if (status > 0)
+ wait(millis);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // caller must check termination
+ }
+ }
+
+ /**
+ * Blocks a non-worker-thread until completion.
+ * @return status upon completion
+ */
+ private int externalAwaitDone() {
+ int s;
+ if ((s = status) >= 0) {
+ boolean interrupted = false;
+ synchronized (this) {
+ while ((s = status) >= 0) {
+ if (s == 0)
+ UNSAFE.compareAndSwapInt(this, statusOffset,
+ 0, SIGNAL);
+ else {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ }
+ }
+ }
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+ return s;
+ }
+
+ /**
+ * Blocks a non-worker-thread until completion or interruption or timeout.
+ */
+ private int externalInterruptibleAwaitDone(long millis)
+ throws InterruptedException {
+ int s;
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ if ((s = status) >= 0) {
+ synchronized (this) {
+ while ((s = status) >= 0) {
+ if (s == 0)
+ UNSAFE.compareAndSwapInt(this, statusOffset,
+ 0, SIGNAL);
+ else {
+ wait(millis);
+ if (millis > 0L)
+ break;
+ }
+ }
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Primary execution method for stolen tasks. Unless done, calls
+ * exec and records status if completed, but doesn't wait for
+ * completion otherwise.
+ */
+ final void doExec() {
+ if (status >= 0) {
+ boolean completed;
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ return;
+ }
+ if (completed)
+ setCompletion(NORMAL); // must be outside try block
+ }
+ }
+
+ /**
+ * Primary mechanics for join, get, quietlyJoin.
+ * @return status upon completion
+ */
+ private int doJoin() {
+ Thread t; ForkJoinWorkerThread w; int s; boolean completed;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ if ((s = status) < 0)
+ return s;
+ if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
+ }
+ if (completed)
+ return setCompletion(NORMAL);
+ }
+ return w.joinTask(this);
+ }
+ else
+ return externalAwaitDone();
+ }
+
+ /**
+ * Primary mechanics for invoke, quietlyInvoke.
+ * @return status upon completion
+ */
+ private int doInvoke() {
+ int s; boolean completed;
+ if ((s = status) < 0)
+ return s;
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
+ }
+ if (completed)
+ return setCompletion(NORMAL);
+ else
+ return doJoin();
+ }
+
+ // Exception table support
+
+ /**
+ * Table of exceptions thrown by tasks, to enable reporting by
+ * callers. Because exceptions are rare, we don't directly keep
+ * them with task objects, but instead use a weak ref table. Note
+ * that cancellation exceptions don't appear in the table, but are
+ * instead recorded as status values.
+ *
+ * Note: These statics are initialized below in static block.
+ */
+ private static final ExceptionNode[] exceptionTable;
+ private static final ReentrantLock exceptionTableLock;
+ private static final ReferenceQueue<Object> exceptionTableRefQueue;
+
+ /**
+ * Fixed capacity for exceptionTable.
+ */
+ private static final int EXCEPTION_MAP_CAPACITY = 32;
+
+ /**
+ * Key-value nodes for exception table. The chained hash table
+ * uses identity comparisons, full locking, and weak references
+ * for keys. The table has a fixed capacity because it only
+ * maintains task exceptions long enough for joiners to access
+ * them, so should never become very large for sustained
+ * periods. However, since we do not know when the last joiner
+ * completes, we must use weak references and expunge them. We do
+ * so on each operation (hence full locking). Also, some thread in
+ * any ForkJoinPool will call helpExpungeStaleExceptions when its
+ * pool becomes isQuiescent.
+ */
+ static final class ExceptionNode extends WeakReference<ForkJoinTask<?>>{
+ final Throwable ex;
+ ExceptionNode next;
+ final long thrower; // use id not ref to avoid weak cycles
+ ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) {
+ super(task, exceptionTableRefQueue);
+ this.ex = ex;
+ this.next = next;
+ this.thrower = Thread.currentThread().getId();
+ }
+ }
+
+ /**
+ * Records exception and sets exceptional completion.
+ *
+ * @return status on exit
+ */
+ private int setExceptionalCompletion(Throwable ex) {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ for (ExceptionNode e = t[i]; ; e = e.next) {
+ if (e == null) {
+ t[i] = new ExceptionNode(this, ex, t[i]);
+ break;
+ }
+ if (e.get() == this) // already present
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ return setCompletion(EXCEPTIONAL);
+ }
+
+ /**
+ * Removes exception node and clears status
+ */
+ private void clearExceptionalCompletion() {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ ExceptionNode e = t[i];
+ ExceptionNode pred = null;
+ while (e != null) {
+ ExceptionNode next = e.next;
+ if (e.get() == this) {
+ if (pred == null)
+ t[i] = next;
+ else
+ pred.next = next;
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ expungeStaleExceptions();
+ status = 0;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns a rethrowable exception for the given task, if
+ * available. To provide accurate stack traces, if the exception
+ * was not thrown by the current thread, we try to create a new
+ * exception of the same type as the one thrown, but with the
+ * recorded exception as its cause. If there is no such
+ * constructor, we instead try to use a no-arg constructor,
+ * followed by initCause, to the same effect. If none of these
+ * apply, or any fail due to other exceptions, we return the
+ * recorded exception, which is still correct, although it may
+ * contain a misleading stack trace.
+ *
+ * @return the exception, or null if none
+ */
+ private Throwable getThrowableException() {
+ if (status != EXCEPTIONAL)
+ return null;
+ int h = System.identityHashCode(this);
+ ExceptionNode e;
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ e = t[h & (t.length - 1)];
+ while (e != null && e.get() != this)
+ e = e.next;
+ } finally {
+ lock.unlock();
+ }
+ Throwable ex;
+ if (e == null || (ex = e.ex) == null)
+ return null;
+ if (e.thrower != Thread.currentThread().getId()) {
+ Class<? extends Throwable> ec = ex.getClass();
+ try {
+ Constructor<?> noArgCtor = null;
+ Constructor<?>[] cs = ec.getConstructors();// public ctors only
+ for (int i = 0; i < cs.length; ++i) {
+ Constructor<?> c = cs[i];
+ Class<?>[] ps = c.getParameterTypes();
+ if (ps.length == 0)
+ noArgCtor = c;
+ else if (ps.length == 1 && ps[0] == Throwable.class)
+ return (Throwable)(c.newInstance(ex));
+ }
+ if (noArgCtor != null) {
+ Throwable wx = (Throwable)(noArgCtor.newInstance());
+ wx.initCause(ex);
+ return wx;
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ return ex;
+ }
+
+ /**
+ * Poll stale refs and remove them. Call only while holding lock.
+ */
+ private static void expungeStaleExceptions() {
+ for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
+ if (x instanceof ExceptionNode) {
+ ForkJoinTask<?> key = ((ExceptionNode)x).get();
+ ExceptionNode[] t = exceptionTable;
+ int i = System.identityHashCode(key) & (t.length - 1);
+ ExceptionNode e = t[i];
+ ExceptionNode pred = null;
+ while (e != null) {
+ ExceptionNode next = e.next;
+ if (e == x) {
+ if (pred == null)
+ t[i] = next;
+ else
+ pred.next = next;
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ }
+ }
+ }
+
+ /**
+ * If lock is available, poll stale refs and remove them.
+ * Called from ForkJoinPool when pools become quiescent.
+ */
+ static final void helpExpungeStaleExceptions() {
+ final ReentrantLock lock = exceptionTableLock;
+ if (lock.tryLock()) {
+ try {
+ expungeStaleExceptions();
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Report the result of invoke or join; called only upon
+ * non-normal return of internal versions.
+ */
+ private V reportResult() {
+ int s; Throwable ex;
+ if ((s = status) == CANCELLED)
+ throw new CancellationException();
+ if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ return getRawResult();
+ }
+
+ // public methods
+
+ /**
+ * Arranges to asynchronously execute this task. While it is not
+ * necessarily enforced, it is a usage error to fork a task more
+ * than once unless it has completed and been reinitialized.
+ * Subsequent modifications to the state of this task or any data
+ * it operates on are not necessarily consistently observable by
+ * any thread other than the one executing it unless preceded by a
+ * call to {@link #join} or related methods, or a call to {@link
+ * #isDone} returning {@code true}.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return {@code this}, to simplify usage
+ */
+ public final ForkJoinTask<V> fork() {
+ ((ForkJoinWorkerThread) Thread.currentThread())
+ .pushTask(this);
+ return this;
+ }
+
+ /**
+ * Returns the result of the computation when it {@link #isDone is
+ * done}. This method differs from {@link #get()} in that
+ * abnormal completion results in {@code RuntimeException} or
+ * {@code Error}, not {@code ExecutionException}, and that
+ * interrupts of the calling thread do <em>not</em> cause the
+ * method to abruptly return by throwing {@code
+ * InterruptedException}.
+ *
+ * @return the computed result
+ */
+ public final V join() {
+ if (doJoin() != NORMAL)
+ return reportResult();
+ else
+ return getRawResult();
+ }
+
+ /**
+ * Commences performing this task, awaits its completion if
+ * necessary, and returns its result, or throws an (unchecked)
+ * {@code RuntimeException} or {@code Error} if the underlying
+ * computation did so.
+ *
+ * @return the computed result
+ */
+ public final V invoke() {
+ if (doInvoke() != NORMAL)
+ return reportResult();
+ else
+ return getRawResult();
+ }
+
+ /**
+ * Forks the given tasks, returning when {@code isDone} holds for
+ * each task or an (unchecked) exception is encountered, in which
+ * case the exception is rethrown. If more than one task
+ * encounters an exception, then this method throws any one of
+ * these exceptions. If any task encounters an exception, the
+ * other may be cancelled. However, the execution status of
+ * individual tasks is not guaranteed upon exceptional return. The
+ * status of each task may be obtained using {@link
+ * #getException()} and related methods to check if they have been
+ * cancelled, completed normally or exceptionally, or left
+ * unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param t1 the first task
+ * @param t2 the second task
+ * @throws NullPointerException if any task is null
+ */
+ public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
+ t2.fork();
+ t1.invoke();
+ t2.join();
+ }
+
+ /**
+ * Forks the given tasks, returning when {@code isDone} holds for
+ * each task or an (unchecked) exception is encountered, in which
+ * case the exception is rethrown. If more than one task
+ * encounters an exception, then this method throws any one of
+ * these exceptions. If any task encounters an exception, others
+ * may be cancelled. However, the execution status of individual
+ * tasks is not guaranteed upon exceptional return. The status of
+ * each task may be obtained using {@link #getException()} and
+ * related methods to check if they have been cancelled, completed
+ * normally or exceptionally, or left unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param tasks the tasks
+ * @throws NullPointerException if any task is null
+ */
+ public static void invokeAll(ForkJoinTask<?>... tasks) {
+ Throwable ex = null;
+ int last = tasks.length - 1;
+ for (int i = last; i >= 0; --i) {
+ ForkJoinTask<?> t = tasks[i];
+ if (t == null) {
+ if (ex == null)
+ ex = new NullPointerException();
+ }
+ else if (i != 0)
+ t.fork();
+ else if (t.doInvoke() < NORMAL && ex == null)
+ ex = t.getException();
+ }
+ for (int i = 1; i <= last; ++i) {
+ ForkJoinTask<?> t = tasks[i];
+ if (t != null) {
+ if (ex != null)
+ t.cancel(false);
+ else if (t.doJoin() < NORMAL)
+ ex = t.getException();
+ }
+ }
+ if (ex != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ }
+
+ /**
+ * Forks all tasks in the specified collection, returning when
+ * {@code isDone} holds for each task or an (unchecked) exception
+ * is encountered, in which case the exception is rethrown. If
+ * more than one task encounters an exception, then this method
+ * throws any one of these exceptions. If any task encounters an
+ * exception, others may be cancelled. However, the execution
+ * status of individual tasks is not guaranteed upon exceptional
+ * return. The status of each task may be obtained using {@link
+ * #getException()} and related methods to check if they have been
+ * cancelled, completed normally or exceptionally, or left
+ * unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param tasks the collection of tasks
+ * @return the tasks argument, to simplify usage
+ * @throws NullPointerException if tasks or any element are null
+ */
+ public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) {
+ if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) {
+ invokeAll(tasks.toArray(new ForkJoinTask<?>[tasks.size()]));
+ return tasks;
+ }
+ @SuppressWarnings("unchecked")
+ List<? extends ForkJoinTask<?>> ts =
+ (List<? extends ForkJoinTask<?>>) tasks;
+ Throwable ex = null;
+ int last = ts.size() - 1;
+ for (int i = last; i >= 0; --i) {
+ ForkJoinTask<?> t = ts.get(i);
+ if (t == null) {
+ if (ex == null)
+ ex = new NullPointerException();
+ }
+ else if (i != 0)
+ t.fork();
+ else if (t.doInvoke() < NORMAL && ex == null)
+ ex = t.getException();
+ }
+ for (int i = 1; i <= last; ++i) {
+ ForkJoinTask<?> t = ts.get(i);
+ if (t != null) {
+ if (ex != null)
+ t.cancel(false);
+ else if (t.doJoin() < NORMAL)
+ ex = t.getException();
+ }
+ }
+ if (ex != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ return tasks;
+ }
+
+ /**
+ * Attempts to cancel execution of this task. This attempt will
+ * fail if the task has already completed or could not be
+ * cancelled for some other reason. If successful, and this task
+ * has not started when {@code cancel} is called, execution of
+ * this task is suppressed. After this method returns
+ * successfully, unless there is an intervening call to {@link
+ * #reinitialize}, subsequent calls to {@link #isCancelled},
+ * {@link #isDone}, and {@code cancel} will return {@code true}
+ * and calls to {@link #join} and related methods will result in
+ * {@code CancellationException}.
+ *
+ * <p>This method may be overridden in subclasses, but if so, must
+ * still ensure that these properties hold. In particular, the
+ * {@code cancel} method itself must not throw exceptions.
+ *
+ * <p>This method is designed to be invoked by <em>other</em>
+ * tasks. To terminate the current task, you can just return or
+ * throw an unchecked exception from its computation method, or
+ * invoke {@link #completeExceptionally}.
+ *
+ * @param mayInterruptIfRunning this value has no effect in the
+ * default implementation because interrupts are not used to
+ * control cancellation.
+ *
+ * @return {@code true} if this task is now cancelled
+ */
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return setCompletion(CANCELLED) == CANCELLED;
+ }
+
+ /**
+ * Cancels, ignoring any exceptions thrown by cancel. Used during
+ * worker and pool shutdown. Cancel is spec'ed not to throw any
+ * exceptions, but if it does anyway, we have no recourse during
+ * shutdown, so guard against this case.
+ */
+ final void cancelIgnoringExceptions() {
+ try {
+ cancel(false);
+ } catch (Throwable ignore) {
+ }
+ }
+
+ public final boolean isDone() {
+ return status < 0;
+ }
+
+ public final boolean isCancelled() {
+ return status == CANCELLED;
+ }
+
+ /**
+ * Returns {@code true} if this task threw an exception or was cancelled.
+ *
+ * @return {@code true} if this task threw an exception or was cancelled
+ */
+ public final boolean isCompletedAbnormally() {
+ return status < NORMAL;
+ }
+
+ /**
+ * Returns {@code true} if this task completed without throwing an
+ * exception and was not cancelled.
+ *
+ * @return {@code true} if this task completed without throwing an
+ * exception and was not cancelled
+ */
+ public final boolean isCompletedNormally() {
+ return status == NORMAL;
+ }
+
+ /**
+ * Returns the exception thrown by the base computation, or a
+ * {@code CancellationException} if cancelled, or {@code null} if
+ * none or if the method has not yet completed.
+ *
+ * @return the exception, or {@code null} if none
+ */
+ public final Throwable getException() {
+ int s = status;
+ return ((s >= NORMAL) ? null :
+ (s == CANCELLED) ? new CancellationException() :
+ getThrowableException());
+ }
+
+ /**
+ * Completes this task abnormally, and if not already aborted or
+ * cancelled, causes it to throw the given exception upon
+ * {@code join} and related operations. This method may be used
+ * to induce exceptions in asynchronous tasks, or to force
+ * completion of tasks that would not otherwise complete. Its use
+ * in other situations is discouraged. This method is
+ * overridable, but overridden versions must invoke {@code super}
+ * implementation to maintain guarantees.
+ *
+ * @param ex the exception to throw. If this exception is not a
+ * {@code RuntimeException} or {@code Error}, the actual exception
+ * thrown will be a {@code RuntimeException} with cause {@code ex}.
+ */
+ public void completeExceptionally(Throwable ex) {
+ setExceptionalCompletion((ex instanceof RuntimeException) ||
+ (ex instanceof Error) ? ex :
+ new RuntimeException(ex));
+ }
+
+ /**
+ * Completes this task, and if not already aborted or cancelled,
+ * returning the given value as the result of subsequent
+ * invocations of {@code join} and related operations. This method
+ * may be used to provide results for asynchronous tasks, or to
+ * provide alternative handling for tasks that would not otherwise
+ * complete normally. Its use in other situations is
+ * discouraged. This method is overridable, but overridden
+ * versions must invoke {@code super} implementation to maintain
+ * guarantees.
+ *
+ * @param value the result value for this task
+ */
+ public void complete(V value) {
+ try {
+ setRawResult(value);
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ return;
+ }
+ setCompletion(NORMAL);
+ }
+
+ /**
+ * Waits if necessary for the computation to complete, and then
+ * retrieves its result.
+ *
+ * @return the computed result
+ * @throws CancellationException if the computation was cancelled
+ * @throws ExecutionException if the computation threw an
+ * exception
+ * @throws InterruptedException if the current thread is not a
+ * member of a ForkJoinPool and was interrupted while waiting
+ */
+ public final V get() throws InterruptedException, ExecutionException {
+ int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
+ doJoin() : externalInterruptibleAwaitDone(0L);
+ Throwable ex;
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
+ throw new ExecutionException(ex);
+ return getRawResult();
+ }
+
+ /**
+ * Waits if necessary for at most the given time for the computation
+ * to complete, and then retrieves its result, if available.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the computed result
+ * @throws CancellationException if the computation was cancelled
+ * @throws ExecutionException if the computation threw an
+ * exception
+ * @throws InterruptedException if the current thread is not a
+ * member of a ForkJoinPool and was interrupted while waiting
+ * @throws TimeoutException if the wait timed out
+ */
+ public final V get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
+ long nanos = unit.toNanos(timeout);
+ if (status >= 0) {
+ boolean completed = false;
+ if (w.unpushTask(this)) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ }
+ }
+ if (completed)
+ setCompletion(NORMAL);
+ else if (status >= 0 && nanos > 0)
+ w.pool.timedAwaitJoin(this, nanos);
+ }
+ }
+ else {
+ long millis = unit.toMillis(timeout);
+ if (millis > 0)
+ externalInterruptibleAwaitDone(millis);
+ }
+ int s = status;
+ if (s != NORMAL) {
+ Throwable ex;
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s != EXCEPTIONAL)
+ throw new TimeoutException();
+ if ((ex = getThrowableException()) != null)
+ throw new ExecutionException(ex);
+ }
+ return getRawResult();
+ }
+
+ /**
+ * Joins this task, without returning its result or throwing its
+ * exception. This method may be useful when processing
+ * collections of tasks when some have been cancelled or otherwise
+ * known to have aborted.
+ */
+ public final void quietlyJoin() {
+ doJoin();
+ }
+
+ /**
+ * Commences performing this task and awaits its completion if
+ * necessary, without returning its result or throwing its
+ * exception.
+ */
+ public final void quietlyInvoke() {
+ doInvoke();
+ }
+
+ /**
+ * Possibly executes tasks until the pool hosting the current task
+ * {@link ForkJoinPool#isQuiescent is quiescent}. This method may
+ * be of use in designs in which many tasks are forked, but none
+ * are explicitly joined, instead executing them until all are
+ * processed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ */
+ public static void helpQuiesce() {
+ ((ForkJoinWorkerThread) Thread.currentThread())
+ .helpQuiescePool();
+ }
+
+ /**
+ * Resets the internal bookkeeping state of this task, allowing a
+ * subsequent {@code fork}. This method allows repeated reuse of
+ * this task, but only if reuse occurs when this task has either
+ * never been forked, or has been forked, then completed and all
+ * outstanding joins of this task have also completed. Effects
+ * under any other usage conditions are not guaranteed.
+ * This method may be useful when executing
+ * pre-constructed trees of subtasks in loops.
+ *
+ * <p>Upon completion of this method, {@code isDone()} reports
+ * {@code false}, and {@code getException()} reports {@code
+ * null}. However, the value returned by {@code getRawResult} is
+ * unaffected. To clear this value, you can invoke {@code
+ * setRawResult(null)}.
+ */
+ public void reinitialize() {
+ if (status == EXCEPTIONAL)
+ clearExceptionalCompletion();
+ else
+ status = 0;
+ }
+
+ /**
+ * Returns the pool hosting the current task execution, or null
+ * if this task is executing outside of any ForkJoinPool.
+ *
+ * @see #inForkJoinPool
+ * @return the pool, or {@code null} if none
+ */
+ public static ForkJoinPool getPool() {
+ Thread t = Thread.currentThread();
+ return (t instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread) t).pool : null;
+ }
+
+ /**
+ * Returns {@code true} if the current thread is a {@link
+ * ForkJoinWorkerThread} executing as a ForkJoinPool computation.
+ *
+ * @return {@code true} if the current thread is a {@link
+ * ForkJoinWorkerThread} executing as a ForkJoinPool computation,
+ * or {@code false} otherwise
+ */
+ public static boolean inForkJoinPool() {
+ return Thread.currentThread() instanceof ForkJoinWorkerThread;
+ }
+
+ /**
+ * Tries to unschedule this task for execution. This method will
+ * typically succeed if this task is the most recently forked task
+ * by the current thread, and has not commenced executing in
+ * another thread. This method may be useful when arranging
+ * alternative local processing of tasks that could have been, but
+ * were not, stolen.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return {@code true} if unforked
+ */
+ public boolean tryUnfork() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .unpushTask(this);
+ }
+
+ /**
+ * Returns an estimate of the number of tasks that have been
+ * forked by the current worker thread but not yet executed. This
+ * value may be useful for heuristic decisions about whether to
+ * fork other tasks.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the number of tasks
+ */
+ public static int getQueuedTaskCount() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .getQueueSize();
+ }
+
+ /**
+ * Returns an estimate of how many more locally queued tasks are
+ * held by the current worker thread than there are other worker
+ * threads that might steal them. This value may be useful for
+ * heuristic decisions about whether to fork other tasks. In many
+ * usages of ForkJoinTasks, at steady state, each worker should
+ * aim to maintain a small constant surplus (for example, 3) of
+ * tasks, and to process computations locally if this threshold is
+ * exceeded.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the surplus number of tasks, which may be negative
+ */
+ public static int getSurplusQueuedTaskCount() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .getEstimatedSurplusTaskCount();
+ }
+
+ // Extension methods
+
+ /**
+ * Returns the result that would be returned by {@link #join}, even
+ * if this task completed abnormally, or {@code null} if this task
+ * is not known to have been completed. This method is designed
+ * to aid debugging, as well as to support extensions. Its use in
+ * any other context is discouraged.
+ *
+ * @return the result, or {@code null} if not completed
+ */
+ public abstract V getRawResult();
+
+ /**
+ * Forces the given value to be returned as a result. This method
+ * is designed to support extensions, and should not in general be
+ * called otherwise.
+ *
+ * @param value the value
+ */
+ protected abstract void setRawResult(V value);
+
+ /**
+ * Immediately performs the base action of this task. This method
+ * is designed to support extensions, and should not in general be
+ * called otherwise. The return value controls whether this task
+ * is considered to be done normally. It may return false in
+ * asynchronous actions that require explicit invocations of
+ * {@link #complete} to become joinable. It may also throw an
+ * (unchecked) exception to indicate abnormal exit.
+ *
+ * @return {@code true} if completed normally
+ */
+ protected abstract boolean exec();
+
+ /**
+ * Returns, but does not unschedule or execute, a task queued by
+ * the current thread but not yet executed, if one is immediately
+ * available. There is no guarantee that this task will actually
+ * be polled or executed next. Conversely, this method may return
+ * null even if a task exists but cannot be accessed without
+ * contention with other threads. This method is designed
+ * primarily to support extensions, and is unlikely to be useful
+ * otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the next task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> peekNextLocalTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .peekTask();
+ }
+
+ /**
+ * Unschedules and returns, without executing, the next task
+ * queued by the current thread but not yet executed. This method
+ * is designed primarily to support extensions, and is unlikely to
+ * be useful otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the next task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> pollNextLocalTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .pollLocalTask();
+ }
+
+ /**
+ * Unschedules and returns, without executing, the next task
+ * queued by the current thread but not yet executed, if one is
+ * available, or if not available, a task that was forked by some
+ * other thread, if available. Availability may be transient, so a
+ * {@code null} result does not necessarily imply quiescence
+ * of the pool this task is operating in. This method is designed
+ * primarily to support extensions, and is unlikely to be useful
+ * otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return a task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> pollTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .pollTask();
+ }
+
+ /**
+ * Adaptor for Runnables. This implements RunnableFuture
+ * to be compliant with AbstractExecutorService constraints
+ * when used in ForkJoinPool.
+ */
+ static final class AdaptedRunnable<T> extends ForkJoinTask<T>
+ implements RunnableFuture<T> {
+ final Runnable runnable;
+ final T resultOnCompletion;
+ T result;
+ AdaptedRunnable(Runnable runnable, T result) {
+ if (runnable == null) throw new NullPointerException();
+ this.runnable = runnable;
+ this.resultOnCompletion = result;
+ }
+ public T getRawResult() { return result; }
+ public void setRawResult(T v) { result = v; }
+ public boolean exec() {
+ runnable.run();
+ result = resultOnCompletion;
+ return true;
+ }
+ public void run() { invoke(); }
+ private static final long serialVersionUID = 5232453952276885070L;
+ }
+
+ /**
+ * Adaptor for Callables
+ */
+ static final class AdaptedCallable<T> extends ForkJoinTask<T>
+ implements RunnableFuture<T> {
+ final Callable<? extends T> callable;
+ T result;
+ AdaptedCallable(Callable<? extends T> callable) {
+ if (callable == null) throw new NullPointerException();
+ this.callable = callable;
+ }
+ public T getRawResult() { return result; }
+ public void setRawResult(T v) { result = v; }
+ public boolean exec() {
+ try {
+ result = callable.call();
+ return true;
+ } catch (Error err) {
+ throw err;
+ } catch (RuntimeException rex) {
+ throw rex;
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ public void run() { invoke(); }
+ private static final long serialVersionUID = 2838392045355241008L;
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code run}
+ * method of the given {@code Runnable} as its action, and returns
+ * a null result upon {@link #join}.
+ *
+ * @param runnable the runnable action
+ * @return the task
+ */
+ public static ForkJoinTask<?> adapt(Runnable runnable) {
+ return new AdaptedRunnable<Void>(runnable, null);
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code run}
+ * method of the given {@code Runnable} as its action, and returns
+ * the given result upon {@link #join}.
+ *
+ * @param runnable the runnable action
+ * @param result the result upon completion
+ * @return the task
+ */
+ public static <T> ForkJoinTask<T> adapt(Runnable runnable, T result) {
+ return new AdaptedRunnable<T>(runnable, result);
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code call}
+ * method of the given {@code Callable} as its action, and returns
+ * its result upon {@link #join}, translating any checked exceptions
+ * encountered into {@code RuntimeException}.
+ *
+ * @param callable the callable action
+ * @return the task
+ */
+ public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) {
+ return new AdaptedCallable<T>(callable);
+ }
+
+ // Serialization support
+
+ private static final long serialVersionUID = -7721805057305804111L;
+
+ /**
+ * Saves the state to a stream (that is, serializes it).
+ *
+ * @serialData the current run status and the exception thrown
+ * during execution, or {@code null} if none
+ * @param s the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ s.writeObject(getException());
+ }
+
+ /**
+ * Reconstitutes the instance from a stream (that is, deserializes it).
+ *
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ Object ex = s.readObject();
+ if (ex != null)
+ setExceptionalCompletion((Throwable)ex);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long statusOffset;
+ static {
+ exceptionTableLock = new ReentrantLock();
+ exceptionTableRefQueue = new ReferenceQueue<Object>();
+ exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ statusOffset = UNSAFE.objectFieldOffset
+ (ForkJoinTask.class.getDeclaredField("status"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
new file mode 100644
index 0000000..d99ffe9
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -0,0 +1,970 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.Collection;
+import java.util.concurrent.RejectedExecutionException;
+import libcore.util.SneakyThrow;
+
+/**
+ * A thread managed by a {@link ForkJoinPool}, which executes
+ * {@link ForkJoinTask}s.
+ * This class is subclassable solely for the sake of adding
+ * functionality -- there are no overridable methods dealing with
+ * scheduling or execution. However, you can override initialization
+ * and termination methods surrounding the main task processing loop.
+ * If you do create such a subclass, you will also need to supply a
+ * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it
+ * in a {@code ForkJoinPool}.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ForkJoinWorkerThread extends Thread {
+ /*
+ * Overview:
+ *
+ * ForkJoinWorkerThreads are managed by ForkJoinPools and perform
+ * ForkJoinTasks. This class includes bookkeeping in support of
+ * worker activation, suspension, and lifecycle control described
+ * in more detail in the internal documentation of class
+ * ForkJoinPool. And as described further below, this class also
+ * includes special-cased support for some ForkJoinTask
+ * methods. But the main mechanics involve work-stealing:
+ *
+ * Work-stealing queues are special forms of Deques that support
+ * only three of the four possible end-operations -- push, pop,
+ * and deq (aka steal), under the further constraints that push
+ * and pop are called only from the owning thread, while deq may
+ * be called from other threads. (If you are unfamiliar with
+ * them, you probably want to read Herlihy and Shavit's book "The
+ * Art of Multiprocessor programming", chapter 16 describing these
+ * in more detail before proceeding.) The main work-stealing
+ * queue design is roughly similar to those in the papers "Dynamic
+ * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
+ * (http://research.sun.com/scalable/pubs/index.html) and
+ * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
+ * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
+ * The main differences ultimately stem from gc requirements that
+ * we null out taken slots as soon as we can, to maintain as small
+ * a footprint as possible even in programs generating huge
+ * numbers of tasks. To accomplish this, we shift the CAS
+ * arbitrating pop vs deq (steal) from being on the indices
+ * ("queueBase" and "queueTop") to the slots themselves (mainly
+ * via method "casSlotNull()"). So, both a successful pop and deq
+ * mainly entail a CAS of a slot from non-null to null. Because
+ * we rely on CASes of references, we do not need tag bits on
+ * queueBase or queueTop. They are simple ints as used in any
+ * circular array-based queue (see for example ArrayDeque).
+ * Updates to the indices must still be ordered in a way that
+ * guarantees that queueTop == queueBase means the queue is empty,
+ * but otherwise may err on the side of possibly making the queue
+ * appear nonempty when a push, pop, or deq have not fully
+ * committed. Note that this means that the deq operation,
+ * considered individually, is not wait-free. One thief cannot
+ * successfully continue until another in-progress one (or, if
+ * previously empty, a push) completes. However, in the
+ * aggregate, we ensure at least probabilistic non-blockingness.
+ * If an attempted steal fails, a thief always chooses a different
+ * random victim target to try next. So, in order for one thief to
+ * progress, it suffices for any in-progress deq or new push on
+ * any empty queue to complete.
+ *
+ * This approach also enables support for "async mode" where local
+ * task processing is in FIFO, not LIFO order; simply by using a
+ * version of deq rather than pop when locallyFifo is true (as set
+ * by the ForkJoinPool). This allows use in message-passing
+ * frameworks in which tasks are never joined. However neither
+ * mode considers affinities, loads, cache localities, etc, so
+ * rarely provide the best possible performance on a given
+ * machine, but portably provide good throughput by averaging over
+ * these factors. (Further, even if we did try to use such
+ * information, we do not usually have a basis for exploiting
+ * it. For example, some sets of tasks profit from cache
+ * affinities, but others are harmed by cache pollution effects.)
+ *
+ * When a worker would otherwise be blocked waiting to join a
+ * task, it first tries a form of linear helping: Each worker
+ * records (in field currentSteal) the most recent task it stole
+ * from some other worker. Plus, it records (in field currentJoin)
+ * the task it is currently actively joining. Method joinTask uses
+ * these markers to try to find a worker to help (i.e., steal back
+ * a task from and execute it) that could hasten completion of the
+ * actively joined task. In essence, the joiner executes a task
+ * that would be on its own local deque had the to-be-joined task
+ * not been stolen. This may be seen as a conservative variant of
+ * the approach in Wagner & Calder "Leapfrogging: a portable
+ * technique for implementing efficient futures" SIGPLAN Notices,
+ * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
+ * in that: (1) We only maintain dependency links across workers
+ * upon steals, rather than use per-task bookkeeping. This may
+ * require a linear scan of workers array to locate stealers, but
+ * usually doesn't because stealers leave hints (that may become
+ * stale/wrong) of where to locate them. This isolates cost to
+ * when it is needed, rather than adding to per-task overhead.
+ * (2) It is "shallow", ignoring nesting and potentially cyclic
+ * mutual steals. (3) It is intentionally racy: field currentJoin
+ * is updated only while actively joining, which means that we
+ * miss links in the chain during long-lived tasks, GC stalls etc
+ * (which is OK since blocking in such cases is usually a good
+ * idea). (4) We bound the number of attempts to find work (see
+ * MAX_HELP) and fall back to suspending the worker and if
+ * necessary replacing it with another.
+ *
+ * Efficient implementation of these algorithms currently relies
+ * on an uncomfortable amount of "Unsafe" mechanics. To maintain
+ * correct orderings, reads and writes of variable queueBase
+ * require volatile ordering. Variable queueTop need not be
+ * volatile because non-local reads always follow those of
+ * queueBase. Similarly, because they are protected by volatile
+ * queueBase reads, reads of the queue array and its slots by
+ * other threads do not need volatile load semantics, but writes
+ * (in push) require store order and CASes (in pop and deq)
+ * require (volatile) CAS semantics. (Michael, Saraswat, and
+ * Vechev's algorithm has similar properties, but without support
+ * for nulling slots.) Since these combinations aren't supported
+ * using ordinary volatiles, the only way to accomplish these
+ * efficiently is to use direct Unsafe calls. (Using external
+ * AtomicIntegers and AtomicReferenceArrays for the indices and
+ * array is significantly slower because of memory locality and
+ * indirection effects.)
+ *
+ * Further, performance on most platforms is very sensitive to
+ * placement and sizing of the (resizable) queue array. Even
+ * though these queues don't usually become all that big, the
+ * initial size must be large enough to counteract cache
+ * contention effects across multiple queues (especially in the
+ * presence of GC cardmarking). Also, to improve thread-locality,
+ * queues are initialized after starting.
+ */
+
+ /**
+ * Mask for pool indices encoded as shorts
+ */
+ private static final int SMASK = 0xffff;
+
+ /**
+ * Capacity of work-stealing queue array upon initialization.
+ * Must be a power of two. Initial size must be at least 4, but is
+ * padded to minimize cache effects.
+ */
+ private static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+ /**
+ * Maximum size for queue array. Must be a power of two
+ * less than or equal to 1 << (31 - width of array entry) to
+ * ensure lack of index wraparound, but is capped at a lower
+ * value to help users trap runaway computations.
+ */
+ private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+
+ /**
+ * The work-stealing queue array. Size must be a power of two.
+ * Initialized when started (as opposed to when constructed), to
+ * improve memory locality.
+ */
+ ForkJoinTask<?>[] queue;
+
+ /**
+ * The pool this thread works in. Accessed directly by ForkJoinTask.
+ */
+ final ForkJoinPool pool;
+
+ /**
+ * Index (mod queue.length) of next queue slot to push to or pop
+ * from. It is written only by owner thread, and accessed by other
+ * threads only after reading (volatile) queueBase. Both queueTop
+ * and queueBase are allowed to wrap around on overflow, but
+ * (queueTop - queueBase) still estimates size.
+ */
+ int queueTop;
+
+ /**
+ * Index (mod queue.length) of least valid queue slot, which is
+ * always the next position to steal from if nonempty.
+ */
+ volatile int queueBase;
+
+ /**
+ * The index of most recent stealer, used as a hint to avoid
+ * traversal in method helpJoinTask. This is only a hint because a
+ * worker might have had multiple steals and this only holds one
+ * of them (usually the most current). Declared non-volatile,
+ * relying on other prevailing sync to keep reasonably current.
+ */
+ int stealHint;
+
+ /**
+ * Index of this worker in pool array. Set once by pool before
+ * running, and accessed directly by pool to locate this worker in
+ * its workers array.
+ */
+ final int poolIndex;
+
+ /**
+ * Encoded record for pool task waits. Usages are always
+ * surrounded by volatile reads/writes
+ */
+ int nextWait;
+
+ /**
+ * Complement of poolIndex, offset by count of entries of task
+ * waits. Accessed by ForkJoinPool to manage event waiters.
+ */
+ volatile int eventCount;
+
+ /**
+ * Seed for random number generator for choosing steal victims.
+ * Uses Marsaglia xorshift. Must be initialized as nonzero.
+ */
+ int seed;
+
+ /**
+ * Number of steals. Directly accessed (and reset) by pool when
+ * idle.
+ */
+ int stealCount;
+
+ /**
+ * True if this worker should or did terminate
+ */
+ volatile boolean terminate;
+
+ /**
+ * Set to true before LockSupport.park; false on return
+ */
+ volatile boolean parked;
+
+ /**
+ * True if use local fifo, not default lifo, for local polling.
+ * Shadows value from ForkJoinPool.
+ */
+ final boolean locallyFifo;
+
+ /**
+ * The task most recently stolen from another worker (or
+ * submission queue). All uses are surrounded by enough volatile
+ * reads/writes to maintain as non-volatile.
+ */
+ ForkJoinTask<?> currentSteal;
+
+ /**
+ * The task currently being joined, set only when actively trying
+ * to help other stealers in helpJoinTask. All uses are surrounded
+ * by enough volatile reads/writes to maintain as non-volatile.
+ */
+ ForkJoinTask<?> currentJoin;
+
+ /**
+ * Creates a ForkJoinWorkerThread operating in the given pool.
+ *
+ * @param pool the pool this thread works in
+ * @throws NullPointerException if pool is null
+ */
+ protected ForkJoinWorkerThread(ForkJoinPool pool) {
+ super(pool.nextWorkerName());
+ this.pool = pool;
+ int k = pool.registerWorker(this);
+ poolIndex = k;
+ eventCount = ~k & SMASK; // clear wait count
+ locallyFifo = pool.locallyFifo;
+ Thread.UncaughtExceptionHandler ueh = pool.ueh;
+ if (ueh != null)
+ setUncaughtExceptionHandler(ueh);
+ setDaemon(true);
+ }
+
+ // Public methods
+
+ /**
+ * Returns the pool hosting this thread.
+ *
+ * @return the pool
+ */
+ public ForkJoinPool getPool() {
+ return pool;
+ }
+
+ /**
+ * Returns the index number of this thread in its pool. The
+ * returned value ranges from zero to the maximum number of
+ * threads (minus one) that have ever been created in the pool.
+ * This method may be useful for applications that track status or
+ * collect results per-worker rather than per-task.
+ *
+ * @return the index number
+ */
+ public int getPoolIndex() {
+ return poolIndex;
+ }
+
+ // Randomization
+
+ /**
+ * Computes next value for random victim probes and backoffs.
+ * Scans don't require a very high quality generator, but also not
+ * a crummy one. Marsaglia xor-shift is cheap and works well
+ * enough. Note: This is manually inlined in FJP.scan() to avoid
+ * writes inside busy loops.
+ */
+ private int nextSeed() {
+ int r = seed;
+ r ^= r << 13;
+ r ^= r >>> 17;
+ r ^= r << 5;
+ return seed = r;
+ }
+
+ // Run State management
+
+ /**
+ * Initializes internal state after construction but before
+ * processing any tasks. If you override this method, you must
+ * invoke {@code super.onStart()} at the beginning of the method.
+ * Initialization requires care: Most fields must have legal
+ * default values, to ensure that attempted accesses from other
+ * threads work correctly even before this thread starts
+ * processing tasks.
+ */
+ protected void onStart() {
+ queue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
+ int r = ForkJoinPool.workerSeedGenerator.nextInt();
+ seed = (r == 0) ? 1 : r; // must be nonzero
+ }
+
+ /**
+ * Performs cleanup associated with termination of this worker
+ * thread. If you override this method, you must invoke
+ * {@code super.onTermination} at the end of the overridden method.
+ *
+ * @param exception the exception causing this thread to abort due
+ * to an unrecoverable error, or {@code null} if completed normally
+ */
+ protected void onTermination(Throwable exception) {
+ try {
+ terminate = true;
+ cancelTasks();
+ pool.deregisterWorker(this, exception);
+ } catch (Throwable ex) { // Shouldn't ever happen
+ if (exception == null) // but if so, at least rethrown
+ exception = ex;
+ } finally {
+ if (exception != null)
+ SneakyThrow.sneakyThrow(exception); // android-changed
+ }
+ }
+
+ /**
+ * This method is required to be public, but should never be
+ * called explicitly. It performs the main run loop to execute
+ * {@link ForkJoinTask}s.
+ */
+ public void run() {
+ Throwable exception = null;
+ try {
+ onStart();
+ pool.work(this);
+ } catch (Throwable ex) {
+ exception = ex;
+ } finally {
+ onTermination(exception);
+ }
+ }
+
+ /*
+ * Intrinsics-based atomic writes for queue slots. These are
+ * basically the same as methods in AtomicReferenceArray, but
+ * specialized for (1) ForkJoinTask elements (2) requirement that
+ * nullness and bounds checks have already been performed by
+ * callers and (3) effective offsets are known not to overflow
+ * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't
+ * need corresponding version for reads: plain array reads are OK
+ * because they are protected by other volatile reads and are
+ * confirmed by CASes.
+ *
+ * Most uses don't actually call these methods, but instead
+ * contain inlined forms that enable more predictable
+ * optimization. We don't define the version of write used in
+ * pushTask at all, but instead inline there a store-fenced array
+ * slot write.
+ *
+ * Also in most methods, as a performance (not correctness) issue,
+ * we'd like to encourage compilers not to arbitrarily postpone
+ * setting queueTop after writing slot. Currently there is no
+ * intrinsic for arranging this, but using Unsafe putOrderedInt
+ * may be a preferable strategy on some compilers even though its
+ * main effect is a pre-, not post- fence. To simplify possible
+ * changes, the option is left in comments next to the associated
+ * assignments.
+ */
+
+ /**
+ * CASes slot i of array q from t to null. Caller must ensure q is
+ * non-null and index is in range.
+ */
+ private static final boolean casSlotNull(ForkJoinTask<?>[] q, int i,
+ ForkJoinTask<?> t) {
+ return UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null);
+ }
+
+ /**
+ * Performs a volatile write of the given task at given slot of
+ * array q. Caller must ensure q is non-null and index is in
+ * range. This method is used only during resets and backouts.
+ */
+ private static final void writeSlot(ForkJoinTask<?>[] q, int i,
+ ForkJoinTask<?> t) {
+ UNSAFE.putObjectVolatile(q, (i << ASHIFT) + ABASE, t);
+ }
+
+ // queue methods
+
+ /**
+ * Pushes a task. Call only from this thread.
+ *
+ * @param t the task. Caller must ensure non-null.
+ */
+ final void pushTask(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] q; int s, m;
+ if ((q = queue) != null) { // ignore if queue removed
+ long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE;
+ UNSAFE.putOrderedObject(q, u, t);
+ queueTop = s + 1; // or use putOrderedInt
+ if ((s -= queueBase) <= 2)
+ pool.signalWork();
+ else if (s == m)
+ growQueue();
+ }
+ }
+
+ /**
+ * Creates or doubles queue array. Transfers elements by
+ * emulating steals (deqs) from old array and placing, oldest
+ * first, into new array.
+ */
+ private void growQueue() {
+ ForkJoinTask<?>[] oldQ = queue;
+ int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ if (size < INITIAL_QUEUE_CAPACITY)
+ size = INITIAL_QUEUE_CAPACITY;
+ ForkJoinTask<?>[] q = queue = new ForkJoinTask<?>[size];
+ int mask = size - 1;
+ int top = queueTop;
+ int oldMask;
+ if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
+ for (int b = queueBase; b != top; ++b) {
+ long u = ((b & oldMask) << ASHIFT) + ABASE;
+ Object x = UNSAFE.getObjectVolatile(oldQ, u);
+ if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
+ UNSAFE.putObjectVolatile
+ (q, ((b & mask) << ASHIFT) + ABASE, x);
+ }
+ }
+ }
+
+ /**
+ * Tries to take a task from the base of the queue, failing if
+ * empty or contended. Note: Specializations of this code appear
+ * in locallyDeqTask and elsewhere.
+ *
+ * @return a task, or null if none or contended
+ */
+ final ForkJoinTask<?> deqTask() {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ if (queueTop != (b = queueBase) &&
+ (q = queue) != null && // must read q after b
+ (i = (q.length - 1) & b) >= 0 &&
+ (t = q[i]) != null && queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ return null;
+ }
+
+ /**
+ * Tries to take a task from the base of own queue. Called only
+ * by this thread.
+ *
+ * @return a task, or null if none
+ */
+ final ForkJoinTask<?> locallyDeqTask() {
+ ForkJoinTask<?> t; int m, b, i;
+ ForkJoinTask<?>[] q = queue;
+ if (q != null && (m = q.length - 1) >= 0) {
+ while (queueTop != (b = queueBase)) {
+ if ((t = q[i = m & b]) != null &&
+ queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE,
+ t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a popped task, or null if empty.
+ * Called only by this thread.
+ */
+ private ForkJoinTask<?> popTask() {
+ int m;
+ ForkJoinTask<?>[] q = queue;
+ if (q != null && (m = q.length - 1) >= 0) {
+ for (int s; (s = queueTop) != queueBase;) {
+ int i = m & --s;
+ long u = (i << ASHIFT) + ABASE; // raw offset
+ ForkJoinTask<?> t = q[i];
+ if (t == null) // lost to stealer
+ break;
+ if (UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueTop = s; // or putOrderedInt
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Specialized version of popTask to pop only if topmost element
+ * is the given task. Called only by this thread.
+ *
+ * @param t the task. Caller must ensure non-null.
+ */
+ final boolean unpushTask(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] q;
+ int s;
+ if ((q = queue) != null && (s = queueTop) != queueBase &&
+ UNSAFE.compareAndSwapObject
+ (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
+ queueTop = s; // or putOrderedInt
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns next task, or null if empty or contended.
+ */
+ final ForkJoinTask<?> peekTask() {
+ int m;
+ ForkJoinTask<?>[] q = queue;
+ if (q == null || (m = q.length - 1) < 0)
+ return null;
+ int i = locallyFifo ? queueBase : (queueTop - 1);
+ return q[i & m];
+ }
+
+ // Support methods for ForkJoinPool
+
+ /**
+ * Runs the given task, plus any local tasks until queue is empty
+ */
+ final void execTask(ForkJoinTask<?> t) {
+ currentSteal = t;
+ for (;;) {
+ if (t != null)
+ t.doExec();
+ if (queueTop == queueBase)
+ break;
+ t = locallyFifo ? locallyDeqTask() : popTask();
+ }
+ ++stealCount;
+ currentSteal = null;
+ }
+
+ /**
+ * Removes and cancels all tasks in queue. Can be called from any
+ * thread.
+ */
+ final void cancelTasks() {
+ ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks
+ if (cj != null && cj.status >= 0)
+ cj.cancelIgnoringExceptions();
+ ForkJoinTask<?> cs = currentSteal;
+ if (cs != null && cs.status >= 0)
+ cs.cancelIgnoringExceptions();
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = deqTask();
+ if (t != null)
+ t.cancelIgnoringExceptions();
+ }
+ }
+
+ /**
+ * Drains tasks to given collection c.
+ *
+ * @return the number of tasks drained
+ */
+ final int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+ int n = 0;
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = deqTask();
+ if (t != null) {
+ c.add(t);
+ ++n;
+ }
+ }
+ return n;
+ }
+
+ // Support methods for ForkJoinTask
+
+ /**
+ * Returns an estimate of the number of tasks in the queue.
+ */
+ final int getQueueSize() {
+ return queueTop - queueBase;
+ }
+
+ /**
+ * Gets and removes a local task.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> pollLocalTask() {
+ return locallyFifo ? locallyDeqTask() : popTask();
+ }
+
+ /**
+ * Gets and removes a local or stolen task.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> pollTask() {
+ ForkJoinWorkerThread[] ws;
+ ForkJoinTask<?> t = pollLocalTask();
+ if (t != null || (ws = pool.workers) == null)
+ return t;
+ int n = ws.length; // cheap version of FJP.scan
+ int steps = n << 1;
+ int r = nextSeed();
+ int i = 0;
+ while (i < steps) {
+ ForkJoinWorkerThread w = ws[(i++ + r) & (n - 1)];
+ if (w != null && w.queueBase != w.queueTop && w.queue != null) {
+ if ((t = w.deqTask()) != null)
+ return t;
+ i = 0;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The maximum stolen->joining link depth allowed in helpJoinTask,
+ * as well as the maximum number of retries (allowing on average
+ * one staleness retry per level) per attempt to instead try
+ * compensation. Depths for legitimate chains are unbounded, but
+ * we use a fixed constant to avoid (otherwise unchecked) cycles
+ * and bound staleness of traversal parameters at the expense of
+ * sometimes blocking when we could be helping.
+ */
+ private static final int MAX_HELP = 16;
+
+ /**
+ * Possibly runs some tasks and/or blocks, until joinMe is done.
+ *
+ * @param joinMe the task to join
+ * @return completion status on exit
+ */
+ final int joinTask(ForkJoinTask<?> joinMe) {
+ ForkJoinTask<?> prevJoin = currentJoin;
+ currentJoin = joinMe;
+ for (int s, retries = MAX_HELP;;) {
+ if ((s = joinMe.status) < 0) {
+ currentJoin = prevJoin;
+ return s;
+ }
+ if (retries > 0) {
+ if (queueTop != queueBase) {
+ if (!localHelpJoinTask(joinMe))
+ retries = 0; // cannot help
+ }
+ else if (retries == MAX_HELP >>> 1) {
+ --retries; // check uncommon case
+ if (tryDeqAndExec(joinMe) >= 0)
+ Thread.yield(); // for politeness
+ }
+ else
+ retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1;
+ }
+ else {
+ retries = MAX_HELP; // restart if not done
+ pool.tryAwaitJoin(joinMe);
+ }
+ }
+ }
+
+ /**
+ * If present, pops and executes the given task, or any other
+ * cancelled task
+ *
+ * @return false if any other non-cancelled task exists in local queue
+ */
+ private boolean localHelpJoinTask(ForkJoinTask<?> joinMe) {
+ int s, i; ForkJoinTask<?>[] q; ForkJoinTask<?> t;
+ if ((s = queueTop) != queueBase && (q = queue) != null &&
+ (i = (q.length - 1) & --s) >= 0 &&
+ (t = q[i]) != null) {
+ if (t != joinMe && t.status >= 0)
+ return false;
+ if (UNSAFE.compareAndSwapObject
+ (q, (i << ASHIFT) + ABASE, t, null)) {
+ queueTop = s; // or putOrderedInt
+ t.doExec();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers, Traces
+ * currentSteal->currentJoin links looking for a thread working on
+ * a descendant of the given task and with a non-empty queue to
+ * steal back and execute tasks from. The implementation is very
+ * branchy to cope with potential inconsistencies or loops
+ * encountering chains that are stale, unknown, or of length
+ * greater than MAX_HELP links. All of these cases are dealt with
+ * by just retrying by caller.
+ *
+ * @param joinMe the task to join
+ * @return true if ran a task
+ */
+ private boolean helpJoinTask(ForkJoinTask<?> joinMe) {
+ boolean helped = false;
+ int m = pool.scanGuard & SMASK;
+ ForkJoinWorkerThread[] ws = pool.workers;
+ if (ws != null && ws.length > m && joinMe.status >= 0) {
+ int levels = MAX_HELP; // remaining chain length
+ ForkJoinTask<?> task = joinMe; // base of chain
+ outer:for (ForkJoinWorkerThread thread = this;;) {
+ // Try to find v, the stealer of task, by first using hint
+ ForkJoinWorkerThread v = ws[thread.stealHint & m];
+ if (v == null || v.currentSteal != task) {
+ for (int j = 0; ;) { // search array
+ if ((v = ws[j]) != null && v.currentSteal == task) {
+ thread.stealHint = j;
+ break; // save hint for next time
+ }
+ if (++j > m)
+ break outer; // can't find stealer
+ }
+ }
+ // Try to help v, using specialized form of deqTask
+ for (;;) {
+ ForkJoinTask<?>[] q; int b, i;
+ if (joinMe.status < 0)
+ break outer;
+ if ((b = v.queueBase) == v.queueTop ||
+ (q = v.queue) == null ||
+ (i = (q.length-1) & b) < 0)
+ break; // empty
+ long u = (i << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = q[i];
+ if (task.status < 0)
+ break outer; // stale
+ if (t != null && v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ v.queueBase = b + 1;
+ v.stealHint = poolIndex;
+ ForkJoinTask<?> ps = currentSteal;
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ helped = true;
+ }
+ }
+ // Try to descend to find v's stealer
+ ForkJoinTask<?> next = v.currentJoin;
+ if (--levels > 0 && task.status >= 0 &&
+ next != null && next != task) {
+ task = next;
+ thread = v;
+ }
+ else
+ break; // max levels, stale, dead-end, or cyclic
+ }
+ }
+ return helped;
+ }
+
+ /**
+ * Performs an uncommon case for joinTask: If task t is at base of
+ * some workers queue, steals and executes it.
+ *
+ * @param t the task
+ * @return t's status
+ */
+ private int tryDeqAndExec(ForkJoinTask<?> t) {
+ int m = pool.scanGuard & SMASK;
+ ForkJoinWorkerThread[] ws = pool.workers;
+ if (ws != null && ws.length > m && t.status >= 0) {
+ for (int j = 0; j <= m; ++j) {
+ ForkJoinTask<?>[] q; int b, i;
+ ForkJoinWorkerThread v = ws[j];
+ if (v != null &&
+ (b = v.queueBase) != v.queueTop &&
+ (q = v.queue) != null &&
+ (i = (q.length - 1) & b) >= 0 &&
+ q[i] == t) {
+ long u = (i << ASHIFT) + ABASE;
+ if (v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ v.queueBase = b + 1;
+ v.stealHint = poolIndex;
+ ForkJoinTask<?> ps = currentSteal;
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ }
+ break;
+ }
+ }
+ }
+ return t.status;
+ }
+
+ /**
+ * Implements ForkJoinTask.getSurplusQueuedTaskCount(). Returns
+ * an estimate of the number of tasks, offset by a function of
+ * number of idle workers.
+ *
+ * This method provides a cheap heuristic guide for task
+ * partitioning when programmers, frameworks, tools, or languages
+ * have little or no idea about task granularity. In essence by
+ * offering this method, we ask users only about tradeoffs in
+ * overhead vs expected throughput and its variance, rather than
+ * how finely to partition tasks.
+ *
+ * In a steady state strict (tree-structured) computation, each
+ * thread makes available for stealing enough tasks for other
+ * threads to remain active. Inductively, if all threads play by
+ * the same rules, each thread should make available only a
+ * constant number of tasks.
+ *
+ * The minimum useful constant is just 1. But using a value of 1
+ * would require immediate replenishment upon each steal to
+ * maintain enough tasks, which is infeasible. Further,
+ * partitionings/granularities of offered tasks should minimize
+ * steal rates, which in general means that threads nearer the top
+ * of computation tree should generate more than those nearer the
+ * bottom. In perfect steady state, each thread is at
+ * approximately the same level of computation tree. However,
+ * producing extra tasks amortizes the uncertainty of progress and
+ * diffusion assumptions.
+ *
+ * So, users will want to use values larger, but not much larger
+ * than 1 to both smooth over transient shortages and hedge
+ * against uneven progress; as traded off against the cost of
+ * extra task overhead. We leave the user to pick a threshold
+ * value to compare with the results of this call to guide
+ * decisions, but recommend values such as 3.
+ *
+ * When all threads are active, it is on average OK to estimate
+ * surplus strictly locally. In steady-state, if one thread is
+ * maintaining say 2 surplus tasks, then so are others. So we can
+ * just use estimated queue length (although note that (queueTop -
+ * queueBase) can be an overestimate because of stealers lagging
+ * increments of queueBase). However, this strategy alone leads
+ * to serious mis-estimates in some non-steady-state conditions
+ * (ramp-up, ramp-down, other stalls). We can detect many of these
+ * by further considering the number of "idle" threads, that are
+ * known to have zero queued tasks, so compensate by a factor of
+ * (#idle/#active) threads.
+ */
+ final int getEstimatedSurplusTaskCount() {
+ return queueTop - queueBase - pool.idlePerActive();
+ }
+
+ /**
+ * Runs tasks until {@code pool.isQuiescent()}. We piggyback on
+ * pool's active count ctl maintenance, but rather than blocking
+ * when tasks cannot be found, we rescan until all others cannot
+ * find tasks either. The bracketing by pool quiescerCounts
+ * updates suppresses pool auto-shutdown mechanics that could
+ * otherwise prematurely terminate the pool because all threads
+ * appear to be inactive.
+ */
+ final void helpQuiescePool() {
+ boolean active = true;
+ ForkJoinTask<?> ps = currentSteal; // to restore below
+ ForkJoinPool p = pool;
+ p.addQuiescerCount(1);
+ for (;;) {
+ ForkJoinWorkerThread[] ws = p.workers;
+ ForkJoinWorkerThread v = null;
+ int n;
+ if (queueTop != queueBase)
+ v = this;
+ else if (ws != null && (n = ws.length) > 1) {
+ ForkJoinWorkerThread w;
+ int r = nextSeed(); // cheap version of FJP.scan
+ int steps = n << 1;
+ for (int i = 0; i < steps; ++i) {
+ if ((w = ws[(i + r) & (n - 1)]) != null &&
+ w.queueBase != w.queueTop) {
+ v = w;
+ break;
+ }
+ }
+ }
+ if (v != null) {
+ ForkJoinTask<?> t;
+ if (!active) {
+ active = true;
+ p.addActiveCount(1);
+ }
+ if ((t = (v != this) ? v.deqTask() :
+ locallyFifo ? locallyDeqTask() : popTask()) != null) {
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ }
+ }
+ else {
+ if (active) {
+ active = false;
+ p.addActiveCount(-1);
+ }
+ if (p.isQuiescent()) {
+ p.addActiveCount(1);
+ p.addQuiescerCount(-1);
+ break;
+ }
+ }
+ }
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long ABASE;
+ private static final int ASHIFT;
+
+ static {
+ int s;
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> a = ForkJoinTask[].class;
+ ABASE = UNSAFE.arrayBaseOffset(a);
+ s = UNSAFE.arrayIndexScale(a);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/Future.java b/luni/src/main/java/java/util/concurrent/Future.java
index eb84796..6a37729 100644
--- a/luni/src/main/java/java/util/concurrent/Future.java
+++ b/luni/src/main/java/java/util/concurrent/Future.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/FutureTask.java b/luni/src/main/java/java/util/concurrent/FutureTask.java
index 2f2335e..d51881d 100644
--- a/luni/src/main/java/java/util/concurrent/FutureTask.java
+++ b/luni/src/main/java/java/util/concurrent/FutureTask.java
@@ -1,55 +1,116 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import java.util.concurrent.locks.LockSupport;
/**
* A cancellable asynchronous computation. This class provides a base
* implementation of {@link Future}, with methods to start and cancel
* a computation, query to see if the computation is complete, and
* retrieve the result of the computation. The result can only be
- * retrieved when the computation has completed; the <tt>get</tt>
- * method will block if the computation has not yet completed. Once
+ * retrieved when the computation has completed; the {@code get}
+ * methods will block if the computation has not yet completed. Once
* the computation has completed, the computation cannot be restarted
- * or cancelled.
+ * or cancelled (unless the computation is invoked using
+ * {@link #runAndReset}).
*
- * <p>A <tt>FutureTask</tt> can be used to wrap a {@link Callable} or
- * {@link java.lang.Runnable} object. Because <tt>FutureTask</tt>
- * implements <tt>Runnable</tt>, a <tt>FutureTask</tt> can be
- * submitted to an {@link Executor} for execution.
+ * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
+ * {@link Runnable} object. Because {@code FutureTask} implements
+ * {@code Runnable}, a {@code FutureTask} can be submitted to an
+ * {@link Executor} for execution.
*
* <p>In addition to serving as a standalone class, this class provides
- * <tt>protected</tt> functionality that may be useful when creating
+ * {@code protected} functionality that may be useful when creating
* customized task classes.
*
* @since 1.5
* @author Doug Lea
- * @param <V> The result type returned by this FutureTask's <tt>get</tt> method
+ * @param <V> The result type returned by this FutureTask's {@code get} methods
*/
public class FutureTask<V> implements RunnableFuture<V> {
- /** Synchronization control for FutureTask */
- private final Sync sync;
+ /*
+ * Revision notes: This differs from previous versions of this
+ * class that relied on AbstractQueuedSynchronizer, mainly to
+ * avoid surprising users about retaining interrupt status during
+ * cancellation races. Sync control in the current design relies
+ * on a "state" field updated via CAS to track completion, along
+ * with a simple Treiber stack to hold waiting threads.
+ *
+ * Style note: As usual, we bypass overhead of using
+ * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
+ */
+
+ /**
+ * The run state of this task, initially NEW. The run state
+ * transitions to a terminal state only in methods set,
+ * setException, and cancel. During completion, state may take on
+ * transient values of COMPLETING (while outcome is being set) or
+ * INTERRUPTING (only while interrupting the runner to satisfy a
+ * cancel(true)). Transitions from these intermediate to final
+ * states use cheaper ordered/lazy writes because values are unique
+ * and cannot be further modified.
+ *
+ * Possible state transitions:
+ * NEW -> COMPLETING -> NORMAL
+ * NEW -> COMPLETING -> EXCEPTIONAL
+ * NEW -> CANCELLED
+ * NEW -> INTERRUPTING -> INTERRUPTED
+ */
+ private volatile int state;
+ private static final int NEW = 0;
+ private static final int COMPLETING = 1;
+ private static final int NORMAL = 2;
+ private static final int EXCEPTIONAL = 3;
+ private static final int CANCELLED = 4;
+ private static final int INTERRUPTING = 5;
+ private static final int INTERRUPTED = 6;
+
+ /** The underlying callable; nulled out after running */
+ private Callable<V> callable;
+ /** The result to return or exception to throw from get() */
+ private Object outcome; // non-volatile, protected by state reads/writes
+ /** The thread running the callable; CASed during run() */
+ private volatile Thread runner;
+ /** Treiber stack of waiting threads */
+ private volatile WaitNode waiters;
+
+ /**
+ * Returns result or throws exception for completed task.
+ *
+ * @param s completed state value
+ */
+ private V report(int s) throws ExecutionException {
+ Object x = outcome;
+ if (s == NORMAL) {
+ @SuppressWarnings("unchecked") V v = (V)x;
+ return v;
+ }
+ if (s >= CANCELLED)
+ throw new CancellationException();
+ throw new ExecutionException((Throwable)x);
+ }
/**
- * Creates a <tt>FutureTask</tt> that will, upon running, execute the
- * given <tt>Callable</tt>.
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Callable}.
*
* @param callable the callable task
- * @throws NullPointerException if callable is null
+ * @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
- sync = new Sync(callable);
+ this.callable = callable;
+ this.state = NEW; // ensure visibility of callable
}
/**
- * Creates a <tt>FutureTask</tt> that will, upon running, execute the
- * given <tt>Runnable</tt>, and arrange that <tt>get</tt> will return the
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
@@ -57,29 +118,46 @@ public class FutureTask<V> implements RunnableFuture<V> {
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
- * @throws NullPointerException if runnable is null
+ * @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
- sync = new Sync(Executors.callable(runnable, result));
+ this.callable = Executors.callable(runnable, result);
+ this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {
- return sync.innerIsCancelled();
+ return state >= CANCELLED;
}
public boolean isDone() {
- return sync.innerIsDone();
+ return state != NEW;
}
public boolean cancel(boolean mayInterruptIfRunning) {
- return sync.innerCancel(mayInterruptIfRunning);
+ if (state != NEW)
+ return false;
+ if (mayInterruptIfRunning) {
+ if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
+ return false;
+ Thread t = runner;
+ if (t != null)
+ t.interrupt();
+ UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
+ }
+ else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
+ return false;
+ finishCompletion();
+ return true;
}
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
- return sync.innerGet();
+ int s = state;
+ if (s <= COMPLETING)
+ s = awaitDone(false, 0L);
+ return report(s);
}
/**
@@ -87,12 +165,18 @@ public class FutureTask<V> implements RunnableFuture<V> {
*/
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- return sync.innerGet(unit.toNanos(timeout));
+ if (unit == null)
+ throw new NullPointerException();
+ int s = state;
+ if (s <= COMPLETING &&
+ (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
+ throw new TimeoutException();
+ return report(s);
}
/**
* Protected method invoked when this task transitions to state
- * <tt>isDone</tt> (whether normally or via cancellation). The
+ * {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
@@ -102,230 +186,268 @@ public class FutureTask<V> implements RunnableFuture<V> {
protected void done() { }
/**
- * Sets the result of this Future to the given value unless
+ * Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
- * This method is invoked internally by the <tt>run</tt> method
+ *
+ * <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
+ *
* @param v the value
*/
protected void set(V v) {
- sync.innerSet(v);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = v;
+ UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
+ finishCompletion();
+ }
}
/**
- * Causes this future to report an <tt>ExecutionException</tt>
- * with the given throwable as its cause, unless this Future has
+ * Causes this future to report an {@link ExecutionException}
+ * with the given throwable as its cause, unless this future has
* already been set or has been cancelled.
- * This method is invoked internally by the <tt>run</tt> method
+ *
+ * <p>This method is invoked internally by the {@link #run} method
* upon failure of the computation.
+ *
* @param t the cause of failure
*/
protected void setException(Throwable t) {
- sync.innerSetException(t);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = t;
+ UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
+ finishCompletion();
+ }
}
- // The following (duplicated) doc comment can be removed once
- //
- // 6270645: Javadoc comments should be inherited from most derived
- // superinterface or superclass
- // is fixed.
- /**
- * Sets this Future to the result of its computation
- * unless it has been cancelled.
- */
public void run() {
- sync.innerRun();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return;
+ try {
+ Callable<V> c = callable;
+ if (c != null && state == NEW) {
+ V result;
+ boolean ran;
+ try {
+ result = c.call();
+ ran = true;
+ } catch (Throwable ex) {
+ result = null;
+ ran = false;
+ setException(ex);
+ }
+ if (ran)
+ set(result);
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ int s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
}
/**
* Executes the computation without setting its result, and then
- * resets this Future to initial state, failing to do so if the
+ * resets this future to initial state, failing to do so if the
* computation encounters an exception or is cancelled. This is
* designed for use with tasks that intrinsically execute more
* than once.
+ *
* @return true if successfully run and reset
*/
protected boolean runAndReset() {
- return sync.innerRunAndReset();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return false;
+ boolean ran = false;
+ int s = state;
+ try {
+ Callable<V> c = callable;
+ if (c != null && s == NEW) {
+ try {
+ c.call(); // don't set result
+ ran = true;
+ } catch (Throwable ex) {
+ setException(ex);
+ }
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
+ return ran && s == NEW;
}
/**
- * Synchronization control for FutureTask. Note that this must be
- * a non-static inner class in order to invoke the protected
- * <tt>done</tt> method. For clarity, all inner class support
- * methods are same as outer, prefixed with "inner".
- *
- * Uses AQS sync state to represent run status
+ * Ensures that any interrupt from a possible cancel(true) is only
+ * delivered to a task while in run or runAndReset.
*/
- private final class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = -7828117401763700385L;
-
- /** State value representing that task is ready to run */
- private static final int READY = 0;
- /** State value representing that task is running */
- private static final int RUNNING = 1;
- /** State value representing that task ran */
- private static final int RAN = 2;
- /** State value representing that task was cancelled */
- private static final int CANCELLED = 4;
-
- /** The underlying callable */
- private final Callable<V> callable;
- /** The result to return from get() */
- private V result;
- /** The exception to throw from get() */
- private Throwable exception;
-
- /**
- * The thread running task. When nulled after set/cancel, this
- * indicates that the results are accessible. Must be
- * volatile, to ensure visibility upon completion.
- */
- private volatile Thread runner;
-
- Sync(Callable<V> callable) {
- this.callable = callable;
- }
-
- private boolean ranOrCancelled(int state) {
- return (state & (RAN | CANCELLED)) != 0;
- }
-
- /**
- * Implements AQS base acquire to succeed if ran or cancelled
- */
- protected int tryAcquireShared(int ignore) {
- return innerIsDone() ? 1 : -1;
- }
-
- /**
- * Implements AQS base release to always signal after setting
- * final done status by nulling runner thread.
- */
- protected boolean tryReleaseShared(int ignore) {
- runner = null;
- return true;
- }
-
- boolean innerIsCancelled() {
- return getState() == CANCELLED;
- }
-
- boolean innerIsDone() {
- return ranOrCancelled(getState()) && runner == null;
- }
-
- V innerGet() throws InterruptedException, ExecutionException {
- acquireSharedInterruptibly(0);
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
+ private void handlePossibleCancellationInterrupt(int s) {
+ // It is possible for our interrupter to stall before getting a
+ // chance to interrupt us. Let's spin-wait patiently.
+ if (s == INTERRUPTING)
+ while (state == INTERRUPTING)
+ Thread.yield(); // wait out pending interrupt
+
+ // assert state == INTERRUPTED;
+
+ // We want to clear any interrupt we may have received from
+ // cancel(true). However, it is permissible to use interrupts
+ // as an independent mechanism for a task to communicate with
+ // its caller, and there is no way to clear only the
+ // cancellation interrupt.
+ //
+ // Thread.interrupted();
+ }
- V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
- if (!tryAcquireSharedNanos(0, nanosTimeout))
- throw new TimeoutException();
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
+ /**
+ * Simple linked list nodes to record waiting threads in a Treiber
+ * stack. See other classes such as Phaser and SynchronousQueue
+ * for more detailed explanation.
+ */
+ static final class WaitNode {
+ volatile Thread thread;
+ volatile WaitNode next;
+ WaitNode() { thread = Thread.currentThread(); }
+ }
- void innerSet(V v) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
- }
- if (compareAndSetState(s, RAN)) {
- result = v;
- releaseShared(0);
- done();
- return;
+ /**
+ * Removes and signals all waiting threads, invokes done(), and
+ * nulls out callable.
+ */
+ private void finishCompletion() {
+ // assert state > COMPLETING;
+ for (WaitNode q; (q = waiters) != null;) {
+ if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
+ for (;;) {
+ Thread t = q.thread;
+ if (t != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ WaitNode next = q.next;
+ if (next == null)
+ break;
+ q.next = null; // unlink to help gc
+ q = next;
}
+ break;
}
}
- void innerSetException(Throwable t) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
- }
- if (compareAndSetState(s, RAN)) {
- exception = t;
- releaseShared(0);
- done();
- return;
- }
+ done();
+
+ callable = null; // to reduce footprint
+ }
+
+ /**
+ * Awaits completion or aborts on interrupt or timeout.
+ *
+ * @param timed true if use timed waits
+ * @param nanos time to wait, if timed
+ * @return state upon completion
+ */
+ private int awaitDone(boolean timed, long nanos)
+ throws InterruptedException {
+ long last = timed ? System.nanoTime() : 0L;
+ WaitNode q = null;
+ boolean queued = false;
+ for (;;) {
+ if (Thread.interrupted()) {
+ removeWaiter(q);
+ throw new InterruptedException();
}
- }
- boolean innerCancel(boolean mayInterruptIfRunning) {
- for (;;) {
- int s = getState();
- if (ranOrCancelled(s))
- return false;
- if (compareAndSetState(s, CANCELLED))
- break;
+ int s = state;
+ if (s > COMPLETING) {
+ if (q != null)
+ q.thread = null;
+ return s;
}
- if (mayInterruptIfRunning) {
- Thread r = runner;
- if (r != null)
- r.interrupt();
+ else if (q == null)
+ q = new WaitNode();
+ else if (!queued)
+ queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q.next = waiters, q);
+ else if (timed) {
+ long now = System.nanoTime();
+ if ((nanos -= (now - last)) <= 0L) {
+ removeWaiter(q);
+ return state;
+ }
+ last = now;
+ LockSupport.parkNanos(this, nanos);
}
- releaseShared(0);
- done();
- return true;
+ else
+ LockSupport.park(this);
}
+ }
- void innerRun() {
- if (!compareAndSetState(READY, RUNNING))
- return;
-
- runner = Thread.currentThread();
- if (getState() == RUNNING) { // recheck after setting thread
- V result;
- try {
- result = callable.call();
- } catch (Throwable ex) {
- setException(ex);
- return;
+ /**
+ * Tries to unlink a timed-out or interrupted wait node to avoid
+ * accumulating garbage. Internal nodes are simply unspliced
+ * without CAS since it is harmless if they are traversed anyway
+ * by releasers. To avoid effects of unsplicing from already
+ * removed nodes, the list is retraversed in case of an apparent
+ * race. This is slow when there are a lot of nodes, but we don't
+ * expect lists to be long enough to outweigh higher-overhead
+ * schemes.
+ */
+ private void removeWaiter(WaitNode node) {
+ if (node != null) {
+ node.thread = null;
+ retry:
+ for (;;) { // restart on removeWaiter race
+ for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
+ s = q.next;
+ if (q.thread != null)
+ pred = q;
+ else if (pred != null) {
+ pred.next = s;
+ if (pred.thread == null) // check for race
+ continue retry;
+ }
+ else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q, s))
+ continue retry;
}
- set(result);
- } else {
- releaseShared(0); // cancel
+ break;
}
}
+ }
- boolean innerRunAndReset() {
- if (!compareAndSetState(READY, RUNNING))
- return false;
- try {
- runner = Thread.currentThread();
- if (getState() == RUNNING)
- callable.call(); // don't set result
- runner = null;
- return compareAndSetState(RUNNING, READY);
- } catch (Throwable ex) {
- setException(ex);
- return false;
- }
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long stateOffset;
+ private static final long runnerOffset;
+ private static final long waitersOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = FutureTask.class;
+ stateOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("state"));
+ runnerOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("runner"));
+ waitersOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("waiters"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
+
}
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
index 8c01e71..6f32c47 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -13,6 +13,10 @@ import java.util.NoSuchElementException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
* linked nodes.
@@ -34,10 +38,6 @@ import java.util.concurrent.locks.ReentrantLock;
* <em>optional</em> methods of the {@link Collection} and {@link
* Iterator} interfaces.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @since 1.6
* @author Doug Lea
* @param <E> the type of elements held in this collection
@@ -590,7 +590,7 @@ public class LinkedBlockingDeque<E>
/**
* Inserts the specified element at the end of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
- * it is generally preferable to use method {@link #offer offer}.
+ * it is generally preferable to use method {@link #offer(Object) offer}.
*
* <p>This method is equivalent to {@link #addLast}.
*
@@ -713,6 +713,8 @@ public class LinkedBlockingDeque<E>
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
+ if (maxElements <= 0)
+ return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -891,8 +893,7 @@ public class LinkedBlockingDeque<E>
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1014,7 +1015,7 @@ public class LinkedBlockingDeque<E>
/**
* The next node to return in next()
*/
- Node<E> next;
+ Node<E> next;
/**
* nextItem holds on to item fields because once we claim that
@@ -1122,7 +1123,7 @@ public class LinkedBlockingDeque<E>
}
/**
- * Save the state of this deque to a stream (that is, serialize it).
+ * Saves the state of this deque to a stream (that is, serializes it).
*
* @serialData The capacity (int), followed by elements (each an
* {@code Object}) in the proper order, followed by a null
@@ -1146,8 +1147,8 @@ public class LinkedBlockingDeque<E>
}
/**
- * Reconstitute this deque from a stream (that is,
- * deserialize it).
+ * Reconstitutes this deque from a stream (that is, deserializes it).
+ *
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
index d06c737..e8c9edb 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -106,13 +106,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
private final int capacity;
/** Current number of elements */
- private final AtomicInteger count = new AtomicInteger(0);
+ private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
- private transient Node<E> head;
+ transient Node<E> head;
/**
* Tail of linked list.
@@ -303,7 +303,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
- Node<E> node = new Node(e);
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
@@ -383,7 +383,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
if (count.get() == capacity)
return false;
int c = -1;
- Node<E> node = new Node(e);
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
@@ -601,8 +601,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -699,6 +698,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
+ if (maxElements <= 0)
+ return 0;
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
@@ -746,7 +747,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
- return new Itr();
+ return new Itr();
}
private class Itr implements Iterator<E> {
@@ -829,7 +830,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Save the state to a stream (that is, serialize it).
+ * Saves the state to a stream (that is, serializes it).
*
* @serialData The capacity is emitted (int), followed by all of
* its elements (each an {@code Object}) in the proper order,
@@ -856,8 +857,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Reconstitute this queue instance from a stream (that is,
- * deserialize it).
+ * Reconstitutes this queue from a stream (that is, deserializes it).
*
* @param s the stream
*/
diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
new file mode 100644
index 0000000..2a3446e
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
@@ -0,0 +1,1323 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+/**
+ * An unbounded {@link TransferQueue} based on linked nodes.
+ * This queue orders elements FIFO (first-in-first-out) with respect
+ * to any given producer. The <em>head</em> of the queue is that
+ * element that has been on the queue the longest time for some
+ * producer. The <em>tail</em> of the queue is that element that has
+ * been on the queue the shortest time for some producer.
+ *
+ * <p>Beware that, unlike in most collections, the {@code size} method
+ * is <em>NOT</em> a constant-time operation. Because of the
+ * asynchronous nature of these queues, determining the current number
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
+ *
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
+ *
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code LinkedTransferQueue}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code LinkedTransferQueue} in another thread.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+public class LinkedTransferQueue<E> extends AbstractQueue<E>
+ implements TransferQueue<E>, java.io.Serializable {
+ private static final long serialVersionUID = -3223113410248163686L;
+
+ /*
+ * *** Overview of Dual Queues with Slack ***
+ *
+ * Dual Queues, introduced by Scherer and Scott
+ * (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf) are
+ * (linked) queues in which nodes may represent either data or
+ * requests. When a thread tries to enqueue a data node, but
+ * encounters a request node, it instead "matches" and removes it;
+ * and vice versa for enqueuing requests. Blocking Dual Queues
+ * arrange that threads enqueuing unmatched requests block until
+ * other threads provide the match. Dual Synchronous Queues (see
+ * Scherer, Lea, & Scott
+ * http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf)
+ * additionally arrange that threads enqueuing unmatched data also
+ * block. Dual Transfer Queues support all of these modes, as
+ * dictated by callers.
+ *
+ * A FIFO dual queue may be implemented using a variation of the
+ * Michael & Scott (M&S) lock-free queue algorithm
+ * (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf).
+ * It maintains two pointer fields, "head", pointing to a
+ * (matched) node that in turn points to the first actual
+ * (unmatched) queue node (or null if empty); and "tail" that
+ * points to the last node on the queue (or again null if
+ * empty). For example, here is a possible queue with four data
+ * elements:
+ *
+ * head tail
+ * | |
+ * v v
+ * M -> U -> U -> U -> U
+ *
+ * The M&S queue algorithm is known to be prone to scalability and
+ * overhead limitations when maintaining (via CAS) these head and
+ * tail pointers. This has led to the development of
+ * contention-reducing variants such as elimination arrays (see
+ * Moir et al http://portal.acm.org/citation.cfm?id=1074013) and
+ * optimistic back pointers (see Ladan-Mozes & Shavit
+ * http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf).
+ * However, the nature of dual queues enables a simpler tactic for
+ * improving M&S-style implementations when dual-ness is needed.
+ *
+ * In a dual queue, each node must atomically maintain its match
+ * status. While there are other possible variants, we implement
+ * this here as: for a data-mode node, matching entails CASing an
+ * "item" field from a non-null data value to null upon match, and
+ * vice-versa for request nodes, CASing from null to a data
+ * value. (Note that the linearization properties of this style of
+ * queue are easy to verify -- elements are made available by
+ * linking, and unavailable by matching.) Compared to plain M&S
+ * queues, this property of dual queues requires one additional
+ * successful atomic operation per enq/deq pair. But it also
+ * enables lower cost variants of queue maintenance mechanics. (A
+ * variation of this idea applies even for non-dual queues that
+ * support deletion of interior elements, such as
+ * j.u.c.ConcurrentLinkedQueue.)
+ *
+ * Once a node is matched, its match status can never again
+ * change. We may thus arrange that the linked list of them
+ * contain a prefix of zero or more matched nodes, followed by a
+ * suffix of zero or more unmatched nodes. (Note that we allow
+ * both the prefix and suffix to be zero length, which in turn
+ * means that we do not use a dummy header.) If we were not
+ * concerned with either time or space efficiency, we could
+ * correctly perform enqueue and dequeue operations by traversing
+ * from a pointer to the initial node; CASing the item of the
+ * first unmatched node on match and CASing the next field of the
+ * trailing node on appends. (Plus some special-casing when
+ * initially empty). While this would be a terrible idea in
+ * itself, it does have the benefit of not requiring ANY atomic
+ * updates on head/tail fields.
+ *
+ * We introduce here an approach that lies between the extremes of
+ * never versus always updating queue (head and tail) pointers.
+ * This offers a tradeoff between sometimes requiring extra
+ * traversal steps to locate the first and/or last unmatched
+ * nodes, versus the reduced overhead and contention of fewer
+ * updates to queue pointers. For example, a possible snapshot of
+ * a queue is:
+ *
+ * head tail
+ * | |
+ * v v
+ * M -> M -> U -> U -> U -> U
+ *
+ * The best value for this "slack" (the targeted maximum distance
+ * between the value of "head" and the first unmatched node, and
+ * similarly for "tail") is an empirical matter. We have found
+ * that using very small constants in the range of 1-3 work best
+ * over a range of platforms. Larger values introduce increasing
+ * costs of cache misses and risks of long traversal chains, while
+ * smaller values increase CAS contention and overhead.
+ *
+ * Dual queues with slack differ from plain M&S dual queues by
+ * virtue of only sometimes updating head or tail pointers when
+ * matching, appending, or even traversing nodes; in order to
+ * maintain a targeted slack. The idea of "sometimes" may be
+ * operationalized in several ways. The simplest is to use a
+ * per-operation counter incremented on each traversal step, and
+ * to try (via CAS) to update the associated queue pointer
+ * whenever the count exceeds a threshold. Another, that requires
+ * more overhead, is to use random number generators to update
+ * with a given probability per traversal step.
+ *
+ * In any strategy along these lines, because CASes updating
+ * fields may fail, the actual slack may exceed targeted
+ * slack. However, they may be retried at any time to maintain
+ * targets. Even when using very small slack values, this
+ * approach works well for dual queues because it allows all
+ * operations up to the point of matching or appending an item
+ * (hence potentially allowing progress by another thread) to be
+ * read-only, thus not introducing any further contention. As
+ * described below, we implement this by performing slack
+ * maintenance retries only after these points.
+ *
+ * As an accompaniment to such techniques, traversal overhead can
+ * be further reduced without increasing contention of head
+ * pointer updates: Threads may sometimes shortcut the "next" link
+ * path from the current "head" node to be closer to the currently
+ * known first unmatched node, and similarly for tail. Again, this
+ * may be triggered with using thresholds or randomization.
+ *
+ * These ideas must be further extended to avoid unbounded amounts
+ * of costly-to-reclaim garbage caused by the sequential "next"
+ * links of nodes starting at old forgotten head nodes: As first
+ * described in detail by Boehm
+ * (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC
+ * delays noticing that any arbitrarily old node has become
+ * garbage, all newer dead nodes will also be unreclaimed.
+ * (Similar issues arise in non-GC environments.) To cope with
+ * this in our implementation, upon CASing to advance the head
+ * pointer, we set the "next" link of the previous head to point
+ * only to itself; thus limiting the length of connected dead lists.
+ * (We also take similar care to wipe out possibly garbage
+ * retaining values held in other Node fields.) However, doing so
+ * adds some further complexity to traversal: If any "next"
+ * pointer links to itself, it indicates that the current thread
+ * has lagged behind a head-update, and so the traversal must
+ * continue from the "head". Traversals trying to find the
+ * current tail starting from "tail" may also encounter
+ * self-links, in which case they also continue at "head".
+ *
+ * It is tempting in slack-based scheme to not even use CAS for
+ * updates (similarly to Ladan-Mozes & Shavit). However, this
+ * cannot be done for head updates under the above link-forgetting
+ * mechanics because an update may leave head at a detached node.
+ * And while direct writes are possible for tail updates, they
+ * increase the risk of long retraversals, and hence long garbage
+ * chains, which can be much more costly than is worthwhile
+ * considering that the cost difference of performing a CAS vs
+ * write is smaller when they are not triggered on each operation
+ * (especially considering that writes and CASes equally require
+ * additional GC bookkeeping ("write barriers") that are sometimes
+ * more costly than the writes themselves because of contention).
+ *
+ * *** Overview of implementation ***
+ *
+ * We use a threshold-based approach to updates, with a slack
+ * threshold of two -- that is, we update head/tail when the
+ * current pointer appears to be two or more steps away from the
+ * first/last node. The slack value is hard-wired: a path greater
+ * than one is naturally implemented by checking equality of
+ * traversal pointers except when the list has only one element,
+ * in which case we keep slack threshold at one. Avoiding tracking
+ * explicit counts across method calls slightly simplifies an
+ * already-messy implementation. Using randomization would
+ * probably work better if there were a low-quality dirt-cheap
+ * per-thread one available, but even ThreadLocalRandom is too
+ * heavy for these purposes.
+ *
+ * With such a small slack threshold value, it is not worthwhile
+ * to augment this with path short-circuiting (i.e., unsplicing
+ * interior nodes) except in the case of cancellation/removal (see
+ * below).
+ *
+ * We allow both the head and tail fields to be null before any
+ * nodes are enqueued; initializing upon first append. This
+ * simplifies some other logic, as well as providing more
+ * efficient explicit control paths instead of letting JVMs insert
+ * implicit NullPointerExceptions when they are null. While not
+ * currently fully implemented, we also leave open the possibility
+ * of re-nulling these fields when empty (which is complicated to
+ * arrange, for little benefit.)
+ *
+ * All enqueue/dequeue operations are handled by the single method
+ * "xfer" with parameters indicating whether to act as some form
+ * of offer, put, poll, take, or transfer (each possibly with
+ * timeout). The relative complexity of using one monolithic
+ * method outweighs the code bulk and maintenance problems of
+ * using separate methods for each case.
+ *
+ * Operation consists of up to three phases. The first is
+ * implemented within method xfer, the second in tryAppend, and
+ * the third in method awaitMatch.
+ *
+ * 1. Try to match an existing node
+ *
+ * Starting at head, skip already-matched nodes until finding
+ * an unmatched node of opposite mode, if one exists, in which
+ * case matching it and returning, also if necessary updating
+ * head to one past the matched node (or the node itself if the
+ * list has no other unmatched nodes). If the CAS misses, then
+ * a loop retries advancing head by two steps until either
+ * success or the slack is at most two. By requiring that each
+ * attempt advances head by two (if applicable), we ensure that
+ * the slack does not grow without bound. Traversals also check
+ * if the initial head is now off-list, in which case they
+ * start at the new head.
+ *
+ * If no candidates are found and the call was untimed
+ * poll/offer, (argument "how" is NOW) return.
+ *
+ * 2. Try to append a new node (method tryAppend)
+ *
+ * Starting at current tail pointer, find the actual last node
+ * and try to append a new node (or if head was null, establish
+ * the first node). Nodes can be appended only if their
+ * predecessors are either already matched or are of the same
+ * mode. If we detect otherwise, then a new node with opposite
+ * mode must have been appended during traversal, so we must
+ * restart at phase 1. The traversal and update steps are
+ * otherwise similar to phase 1: Retrying upon CAS misses and
+ * checking for staleness. In particular, if a self-link is
+ * encountered, then we can safely jump to a node on the list
+ * by continuing the traversal at current head.
+ *
+ * On successful append, if the call was ASYNC, return.
+ *
+ * 3. Await match or cancellation (method awaitMatch)
+ *
+ * Wait for another thread to match node; instead cancelling if
+ * the current thread was interrupted or the wait timed out. On
+ * multiprocessors, we use front-of-queue spinning: If a node
+ * appears to be the first unmatched node in the queue, it
+ * spins a bit before blocking. In either case, before blocking
+ * it tries to unsplice any nodes between the current "head"
+ * and the first unmatched node.
+ *
+ * Front-of-queue spinning vastly improves performance of
+ * heavily contended queues. And so long as it is relatively
+ * brief and "quiet", spinning does not much impact performance
+ * of less-contended queues. During spins threads check their
+ * interrupt status and generate a thread-local random number
+ * to decide to occasionally perform a Thread.yield. While
+ * yield has underdefined specs, we assume that it might help,
+ * and will not hurt, in limiting impact of spinning on busy
+ * systems. We also use smaller (1/2) spins for nodes that are
+ * not known to be front but whose predecessors have not
+ * blocked -- these "chained" spins avoid artifacts of
+ * front-of-queue rules which otherwise lead to alternating
+ * nodes spinning vs blocking. Further, front threads that
+ * represent phase changes (from data to request node or vice
+ * versa) compared to their predecessors receive additional
+ * chained spins, reflecting longer paths typically required to
+ * unblock threads during phase changes.
+ *
+ *
+ * ** Unlinking removed interior nodes **
+ *
+ * In addition to minimizing garbage retention via self-linking
+ * described above, we also unlink removed interior nodes. These
+ * may arise due to timed out or interrupted waits, or calls to
+ * remove(x) or Iterator.remove. Normally, given a node that was
+ * at one time known to be the predecessor of some node s that is
+ * to be removed, we can unsplice s by CASing the next field of
+ * its predecessor if it still points to s (otherwise s must
+ * already have been removed or is now offlist). But there are two
+ * situations in which we cannot guarantee to make node s
+ * unreachable in this way: (1) If s is the trailing node of list
+ * (i.e., with null next), then it is pinned as the target node
+ * for appends, so can only be removed later after other nodes are
+ * appended. (2) We cannot necessarily unlink s given a
+ * predecessor node that is matched (including the case of being
+ * cancelled): the predecessor may already be unspliced, in which
+ * case some previous reachable node may still point to s.
+ * (For further explanation see Herlihy & Shavit "The Art of
+ * Multiprocessor Programming" chapter 9). Although, in both
+ * cases, we can rule out the need for further action if either s
+ * or its predecessor are (or can be made to be) at, or fall off
+ * from, the head of list.
+ *
+ * Without taking these into account, it would be possible for an
+ * unbounded number of supposedly removed nodes to remain
+ * reachable. Situations leading to such buildup are uncommon but
+ * can occur in practice; for example when a series of short timed
+ * calls to poll repeatedly time out but never otherwise fall off
+ * the list because of an untimed call to take at the front of the
+ * queue.
+ *
+ * When these cases arise, rather than always retraversing the
+ * entire list to find an actual predecessor to unlink (which
+ * won't help for case (1) anyway), we record a conservative
+ * estimate of possible unsplice failures (in "sweepVotes").
+ * We trigger a full sweep when the estimate exceeds a threshold
+ * ("SWEEP_THRESHOLD") indicating the maximum number of estimated
+ * removal failures to tolerate before sweeping through, unlinking
+ * cancelled nodes that were not unlinked upon initial removal.
+ * We perform sweeps by the thread hitting threshold (rather than
+ * background threads or by spreading work to other threads)
+ * because in the main contexts in which removal occurs, the
+ * caller is already timed-out, cancelled, or performing a
+ * potentially O(n) operation (e.g. remove(x)), none of which are
+ * time-critical enough to warrant the overhead that alternatives
+ * would impose on other threads.
+ *
+ * Because the sweepVotes estimate is conservative, and because
+ * nodes become unlinked "naturally" as they fall off the head of
+ * the queue, and because we allow votes to accumulate even while
+ * sweeps are in progress, there are typically significantly fewer
+ * such nodes than estimated. Choice of a threshold value
+ * balances the likelihood of wasted effort and contention, versus
+ * providing a worst-case bound on retention of interior nodes in
+ * quiescent queues. The value defined below was chosen
+ * empirically to balance these under various timeout scenarios.
+ *
+ * Note that we cannot self-link unlinked interior nodes during
+ * sweeps. However, the associated garbage chains terminate when
+ * some successor ultimately falls off the head of the list and is
+ * self-linked.
+ */
+
+ /** True if on multiprocessor */
+ private static final boolean MP =
+ Runtime.getRuntime().availableProcessors() > 1;
+
+ /**
+ * The number of times to spin (with randomly interspersed calls
+ * to Thread.yield) on multiprocessor before blocking when a node
+ * is apparently the first waiter in the queue. See above for
+ * explanation. Must be a power of two. The value is empirically
+ * derived -- it works pretty well across a variety of processors,
+ * numbers of CPUs, and OSes.
+ */
+ private static final int FRONT_SPINS = 1 << 7;
+
+ /**
+ * The number of times to spin before blocking when a node is
+ * preceded by another node that is apparently spinning. Also
+ * serves as an increment to FRONT_SPINS on phase changes, and as
+ * base average frequency for yielding during spins. Must be a
+ * power of two.
+ */
+ private static final int CHAINED_SPINS = FRONT_SPINS >>> 1;
+
+ /**
+ * The maximum number of estimated removal failures (sweepVotes)
+ * to tolerate before sweeping through the queue unlinking
+ * cancelled nodes that were not unlinked upon initial
+ * removal. See above for explanation. The value must be at least
+ * two to avoid useless sweeps when removing trailing nodes.
+ */
+ static final int SWEEP_THRESHOLD = 32;
+
+ /**
+ * Queue nodes. Uses Object, not E, for items to allow forgetting
+ * them after use. Relies heavily on Unsafe mechanics to minimize
+ * unnecessary ordering constraints: Writes that are intrinsically
+ * ordered wrt other accesses or CASes use simple relaxed forms.
+ */
+ static final class Node {
+ final boolean isData; // false if this is a request node
+ volatile Object item; // initially non-null if isData; CASed to match
+ volatile Node next;
+ volatile Thread waiter; // null until waiting
+
+ // CAS methods for fields
+ final boolean casNext(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ }
+
+ final boolean casItem(Object cmp, Object val) {
+ // assert cmp == null || cmp.getClass() != Node.class;
+ return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
+ }
+
+ /**
+ * Constructs a new node. Uses relaxed write because item can
+ * only be seen after publication via casNext.
+ */
+ Node(Object item, boolean isData) {
+ UNSAFE.putObject(this, itemOffset, item); // relaxed write
+ this.isData = isData;
+ }
+
+ /**
+ * Links node to itself to avoid garbage retention. Called
+ * only after CASing head field, so uses relaxed write.
+ */
+ final void forgetNext() {
+ UNSAFE.putObject(this, nextOffset, this);
+ }
+
+ /**
+ * Sets item to self and waiter to null, to avoid garbage
+ * retention after matching or cancelling. Uses relaxed writes
+ * because order is already constrained in the only calling
+ * contexts: item is forgotten only after volatile/atomic
+ * mechanics that extract items. Similarly, clearing waiter
+ * follows either CAS or return from park (if ever parked;
+ * else we don't care).
+ */
+ final void forgetContents() {
+ UNSAFE.putObject(this, itemOffset, this);
+ UNSAFE.putObject(this, waiterOffset, null);
+ }
+
+ /**
+ * Returns true if this node has been matched, including the
+ * case of artificial matches due to cancellation.
+ */
+ final boolean isMatched() {
+ Object x = item;
+ return (x == this) || ((x == null) == isData);
+ }
+
+ /**
+ * Returns true if this is an unmatched request node.
+ */
+ final boolean isUnmatchedRequest() {
+ return !isData && item == null;
+ }
+
+ /**
+ * Returns true if a node with the given mode cannot be
+ * appended to this node because this node is unmatched and
+ * has opposite data mode.
+ */
+ final boolean cannotPrecede(boolean haveData) {
+ boolean d = isData;
+ Object x;
+ return d != haveData && (x = item) != this && (x != null) == d;
+ }
+
+ /**
+ * Tries to artificially match a data node -- used by remove.
+ */
+ final boolean tryMatchData() {
+ // assert isData;
+ Object x = item;
+ if (x != null && x != this && casItem(x, null)) {
+ LockSupport.unpark(waiter);
+ return true;
+ }
+ return false;
+ }
+
+ private static final long serialVersionUID = -3375979862319811754L;
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+ private static final long waiterOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ waiterOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("waiter"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /** head of the queue; null until first enqueue */
+ transient volatile Node head;
+
+ /** tail of the queue; null until first append */
+ private transient volatile Node tail;
+
+ /** The number of apparent failures to unsplice removed nodes */
+ private transient volatile int sweepVotes;
+
+ // CAS methods for fields
+ private boolean casTail(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
+ }
+
+ private boolean casHead(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ }
+
+ private boolean casSweepVotes(int cmp, int val) {
+ return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);
+ }
+
+ /*
+ * Possible values for "how" argument in xfer method.
+ */
+ private static final int NOW = 0; // for untimed poll, tryTransfer
+ private static final int ASYNC = 1; // for offer, put, add
+ private static final int SYNC = 2; // for transfer, take
+ private static final int TIMED = 3; // for timed poll, tryTransfer
+
+ @SuppressWarnings("unchecked")
+ static <E> E cast(Object item) {
+ // assert item == null || item.getClass() != Node.class;
+ return (E) item;
+ }
+
+ /**
+ * Implements all queuing methods. See above for explanation.
+ *
+ * @param e the item or null for take
+ * @param haveData true if this is a put, else a take
+ * @param how NOW, ASYNC, SYNC, or TIMED
+ * @param nanos timeout in nanosecs, used only if mode is TIMED
+ * @return an item if matched, else e
+ * @throws NullPointerException if haveData mode but e is null
+ */
+ private E xfer(E e, boolean haveData, int how, long nanos) {
+ if (haveData && (e == null))
+ throw new NullPointerException();
+ Node s = null; // the node to append, if needed
+
+ retry:
+ for (;;) { // restart on append race
+
+ for (Node h = head, p = h; p != null;) { // find & match first node
+ boolean isData = p.isData;
+ Object item = p.item;
+ if (item != p && (item != null) == isData) { // unmatched
+ if (isData == haveData) // can't match
+ break;
+ if (p.casItem(item, e)) { // match
+ for (Node q = p; q != h;) {
+ Node n = q.next; // update by 2 unless singleton
+ if (head == h && casHead(h, n == null ? q : n)) {
+ h.forgetNext();
+ break;
+ } // advance and retry
+ if ((h = head) == null ||
+ (q = h.next) == null || !q.isMatched())
+ break; // unless slack < 2
+ }
+ LockSupport.unpark(p.waiter);
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ }
+ Node n = p.next;
+ p = (p != n) ? n : (h = head); // Use head if p offlist
+ }
+
+ if (how != NOW) { // No matches available
+ if (s == null)
+ s = new Node(e, haveData);
+ Node pred = tryAppend(s, haveData);
+ if (pred == null)
+ continue retry; // lost race vs opposite mode
+ if (how != ASYNC)
+ return awaitMatch(s, pred, e, (how == TIMED), nanos);
+ }
+ return e; // not waiting
+ }
+ }
+
+ /**
+ * Tries to append node s as tail.
+ *
+ * @param s the node to append
+ * @param haveData true if appending in data mode
+ * @return null on failure due to losing race with append in
+ * different mode, else s's predecessor, or s itself if no
+ * predecessor
+ */
+ private Node tryAppend(Node s, boolean haveData) {
+ for (Node t = tail, p = t;;) { // move p to last node and append
+ Node n, u; // temps for reads of next & tail
+ if (p == null && (p = head) == null) {
+ if (casHead(null, s))
+ return s; // initialize
+ }
+ else if (p.cannotPrecede(haveData))
+ return null; // lost race vs opposite mode
+ else if ((n = p.next) != null) // not last; keep traversing
+ p = p != t && t != (u = tail) ? (t = u) : // stale tail
+ (p != n) ? n : null; // restart if off list
+ else if (!p.casNext(null, s))
+ p = p.next; // re-read on CAS failure
+ else {
+ if (p != t) { // update if slack now >= 2
+ while ((tail != t || !casTail(t, s)) &&
+ (t = tail) != null &&
+ (s = t.next) != null && // advance and retry
+ (s = s.next) != null && s != t);
+ }
+ return p;
+ }
+ }
+ }
+
+ /**
+ * Spins/yields/blocks until node s is matched or caller gives up.
+ *
+ * @param s the waiting node
+ * @param pred the predecessor of s, or s itself if it has no
+ * predecessor, or null if unknown (the null case does not occur
+ * in any current calls but may in possible future extensions)
+ * @param e the comparison value for checking match
+ * @param timed if true, wait only until timeout elapses
+ * @param nanos timeout in nanosecs, used only if timed is true
+ * @return matched item, or e if unmatched on interrupt or timeout
+ */
+ private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {
+ long lastTime = timed ? System.nanoTime() : 0L;
+ Thread w = Thread.currentThread();
+ int spins = -1; // initialized after first item and cancel checks
+ ThreadLocalRandom randomYields = null; // bound if needed
+
+ for (;;) {
+ Object item = s.item;
+ if (item != e) { // matched
+ // assert item != s;
+ s.forgetContents(); // avoid garbage
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ if ((w.isInterrupted() || (timed && nanos <= 0)) &&
+ s.casItem(e, s)) { // cancel
+ unsplice(pred, s);
+ return e;
+ }
+
+ if (spins < 0) { // establish spins at/near front
+ if ((spins = spinsFor(pred, s.isData)) > 0)
+ randomYields = ThreadLocalRandom.current();
+ }
+ else if (spins > 0) { // spin
+ --spins;
+ if (randomYields.nextInt(CHAINED_SPINS) == 0)
+ Thread.yield(); // occasionally yield
+ }
+ else if (s.waiter == null) {
+ s.waiter = w; // request unpark then recheck
+ }
+ else if (timed) {
+ long now = System.nanoTime();
+ if ((nanos -= now - lastTime) > 0)
+ LockSupport.parkNanos(this, nanos);
+ lastTime = now;
+ }
+ else {
+ LockSupport.park(this);
+ }
+ }
+ }
+
+ /**
+ * Returns spin/yield value for a node with given predecessor and
+ * data mode. See above for explanation.
+ */
+ private static int spinsFor(Node pred, boolean haveData) {
+ if (MP && pred != null) {
+ if (pred.isData != haveData) // phase change
+ return FRONT_SPINS + CHAINED_SPINS;
+ if (pred.isMatched()) // probably at front
+ return FRONT_SPINS;
+ if (pred.waiter == null) // pred apparently spinning
+ return CHAINED_SPINS;
+ }
+ return 0;
+ }
+
+ /* -------------- Traversal methods -------------- */
+
+ /**
+ * Returns the successor of p, or the head node if p.next has been
+ * linked to self, which will only be true if traversing with a
+ * stale pointer that is now off the list.
+ */
+ final Node succ(Node p) {
+ Node next = p.next;
+ return (p == next) ? head : next;
+ }
+
+ /**
+ * Returns the first unmatched node of the given mode, or null if
+ * none. Used by methods isEmpty, hasWaitingConsumer.
+ */
+ private Node firstOfMode(boolean isData) {
+ for (Node p = head; p != null; p = succ(p)) {
+ if (!p.isMatched())
+ return (p.isData == isData) ? p : null;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the item in the first unmatched node with isData; or
+ * null if none. Used by peek.
+ */
+ private E firstDataItem() {
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p)
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ else if (item == null)
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * Traverses and counts unmatched nodes of the given mode.
+ * Used by methods size and getWaitingConsumerCount.
+ */
+ private int countOfMode(boolean data) {
+ int count = 0;
+ for (Node p = head; p != null; ) {
+ if (!p.isMatched()) {
+ if (p.isData != data)
+ return 0;
+ if (++count == Integer.MAX_VALUE) // saturated
+ break;
+ }
+ Node n = p.next;
+ if (n != p)
+ p = n;
+ else {
+ count = 0;
+ p = head;
+ }
+ }
+ return count;
+ }
+
+ final class Itr implements Iterator<E> {
+ private Node nextNode; // next node to return item for
+ private E nextItem; // the corresponding item
+ private Node lastRet; // last returned node, to support remove
+ private Node lastPred; // predecessor to unlink lastRet
+
+ /**
+ * Moves to next node after prev, or first node if prev null.
+ */
+ private void advance(Node prev) {
+ /*
+ * To track and avoid buildup of deleted nodes in the face
+ * of calls to both Queue.remove and Itr.remove, we must
+ * include variants of unsplice and sweep upon each
+ * advance: Upon Itr.remove, we may need to catch up links
+ * from lastPred, and upon other removes, we might need to
+ * skip ahead from stale nodes and unsplice deleted ones
+ * found while advancing.
+ */
+
+ Node r, b; // reset lastPred upon possible deletion of lastRet
+ if ((r = lastRet) != null && !r.isMatched())
+ lastPred = r; // next lastPred is old lastRet
+ else if ((b = lastPred) == null || b.isMatched())
+ lastPred = null; // at start of list
+ else {
+ Node s, n; // help with removal of lastPred.next
+ while ((s = b.next) != null &&
+ s != b && s.isMatched() &&
+ (n = s.next) != null && n != s)
+ b.casNext(s, n);
+ }
+
+ this.lastRet = prev;
+
+ for (Node p = prev, s, n;;) {
+ s = (p == null) ? head : p.next;
+ if (s == null)
+ break;
+ else if (s == p) {
+ p = null;
+ continue;
+ }
+ Object item = s.item;
+ if (s.isData) {
+ if (item != null && item != s) {
+ nextItem = LinkedTransferQueue.<E>cast(item);
+ nextNode = s;
+ return;
+ }
+ }
+ else if (item == null)
+ break;
+ // assert s.isMatched();
+ if (p == null)
+ p = s;
+ else if ((n = s.next) == null)
+ break;
+ else if (s == n)
+ p = null;
+ else
+ p.casNext(s, n);
+ }
+ nextNode = null;
+ nextItem = null;
+ }
+
+ Itr() {
+ advance(null);
+ }
+
+ public final boolean hasNext() {
+ return nextNode != null;
+ }
+
+ public final E next() {
+ Node p = nextNode;
+ if (p == null) throw new NoSuchElementException();
+ E e = nextItem;
+ advance(p);
+ return e;
+ }
+
+ public final void remove() {
+ final Node lastRet = this.lastRet;
+ if (lastRet == null)
+ throw new IllegalStateException();
+ this.lastRet = null;
+ if (lastRet.tryMatchData())
+ unsplice(lastPred, lastRet);
+ }
+ }
+
+ /* -------------- Removal methods -------------- */
+
+ /**
+ * Unsplices (now or later) the given deleted/cancelled node with
+ * the given predecessor.
+ *
+ * @param pred a node that was at one time known to be the
+ * predecessor of s, or null or s itself if s is/was at head
+ * @param s the node to be unspliced
+ */
+ final void unsplice(Node pred, Node s) {
+ s.forgetContents(); // forget unneeded fields
+ /*
+ * See above for rationale. Briefly: if pred still points to
+ * s, try to unlink s. If s cannot be unlinked, because it is
+ * trailing node or pred might be unlinked, and neither pred
+ * nor s are head or offlist, add to sweepVotes, and if enough
+ * votes have accumulated, sweep.
+ */
+ if (pred != null && pred != s && pred.next == s) {
+ Node n = s.next;
+ if (n == null ||
+ (n != s && pred.casNext(s, n) && pred.isMatched())) {
+ for (;;) { // check if at, or could be, head
+ Node h = head;
+ if (h == pred || h == s || h == null)
+ return; // at head or list empty
+ if (!h.isMatched())
+ break;
+ Node hn = h.next;
+ if (hn == null)
+ return; // now empty
+ if (hn != h && casHead(h, hn))
+ h.forgetNext(); // advance head
+ }
+ if (pred.next != pred && s.next != s) { // recheck if offlist
+ for (;;) { // sweep now if enough votes
+ int v = sweepVotes;
+ if (v < SWEEP_THRESHOLD) {
+ if (casSweepVotes(v, v + 1))
+ break;
+ }
+ else if (casSweepVotes(v, 0)) {
+ sweep();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Unlinks matched (typically cancelled) nodes encountered in a
+ * traversal from head.
+ */
+ private void sweep() {
+ for (Node p = head, s, n; p != null && (s = p.next) != null; ) {
+ if (!s.isMatched())
+ // Unmatched nodes are never self-linked
+ p = s;
+ else if ((n = s.next) == null) // trailing node is pinned
+ break;
+ else if (s == n) // stale
+ // No need to also check for p == s, since that implies s == n
+ p = head;
+ else
+ p.casNext(s, n);
+ }
+ }
+
+ /**
+ * Main implementation of remove(Object)
+ */
+ private boolean findAndRemove(Object e) {
+ if (e != null) {
+ for (Node pred = null, p = head; p != null; ) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && e.equals(item) &&
+ p.tryMatchData()) {
+ unsplice(pred, p);
+ return true;
+ }
+ }
+ else if (item == null)
+ break;
+ pred = p;
+ if ((p = p.next) == pred) { // stale
+ pred = null;
+ p = head;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Creates an initially empty {@code LinkedTransferQueue}.
+ */
+ public LinkedTransferQueue() {
+ }
+
+ /**
+ * Creates a {@code LinkedTransferQueue}
+ * initially containing the elements of the given collection,
+ * added in traversal order of the collection's iterator.
+ *
+ * @param c the collection of elements to initially contain
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
+ */
+ public LinkedTransferQueue(Collection<? extends E> c) {
+ this();
+ addAll(c);
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never block.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public void put(E e) {
+ xfer(e, true, ASYNC, 0);
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never block or
+ * return {@code false}.
+ *
+ * @return {@code true} (as specified by
+ * {@link java.util.concurrent.BlockingQueue#offer(Object,long,TimeUnit)
+ * BlockingQueue.offer})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e, long timeout, TimeUnit unit) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never return {@code false}.
+ *
+ * @return {@code true} (as specified by {@link Queue#offer})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never throw
+ * {@link IllegalStateException} or return {@code false}.
+ *
+ * @return {@code true} (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean add(E e) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Transfers the element to a waiting consumer immediately, if possible.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * otherwise returning {@code false} without enqueuing the element.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean tryTransfer(E e) {
+ return xfer(e, true, NOW, 0) == null;
+ }
+
+ /**
+ * Transfers the element to a consumer, waiting if necessary to do so.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else inserts the specified element at the tail of this queue
+ * and waits until the element is received by a consumer.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public void transfer(E e) throws InterruptedException {
+ if (xfer(e, true, SYNC, 0) != null) {
+ Thread.interrupted(); // failure possible only due to interrupt
+ throw new InterruptedException();
+ }
+ }
+
+ /**
+ * Transfers the element to a consumer if it is possible to do so
+ * before the timeout elapses.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else inserts the specified element at the tail of this queue
+ * and waits until the element is received by a consumer,
+ * returning {@code false} if the specified wait time elapses
+ * before the element can be transferred.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean tryTransfer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)
+ return true;
+ if (!Thread.interrupted())
+ return false;
+ throw new InterruptedException();
+ }
+
+ public E take() throws InterruptedException {
+ E e = xfer(null, false, SYNC, 0);
+ if (e != null)
+ return e;
+ Thread.interrupted();
+ throw new InterruptedException();
+ }
+
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+ E e = xfer(null, false, TIMED, unit.toNanos(timeout));
+ if (e != null || !Thread.interrupted())
+ return e;
+ throw new InterruptedException();
+ }
+
+ public E poll() {
+ return xfer(null, false, NOW, 0);
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection<? super E> c) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ int n = 0;
+ for (E e; (e = poll()) != null;) {
+ c.add(e);
+ ++n;
+ }
+ return n;
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection<? super E> c, int maxElements) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ int n = 0;
+ for (E e; n < maxElements && (e = poll()) != null;) {
+ c.add(e);
+ ++n;
+ }
+ return n;
+ }
+
+ /**
+ * Returns an iterator over the elements in this queue in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * <p>The returned iterator is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * 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.
+ *
+ * @return an iterator over the elements in this queue in proper sequence
+ */
+ public Iterator<E> iterator() {
+ return new Itr();
+ }
+
+ public E peek() {
+ return firstDataItem();
+ }
+
+ /**
+ * Returns {@code true} if this queue contains no elements.
+ *
+ * @return {@code true} if this queue contains no elements
+ */
+ public boolean isEmpty() {
+ for (Node p = head; p != null; p = succ(p)) {
+ if (!p.isMatched())
+ return !p.isData;
+ }
+ return true;
+ }
+
+ public boolean hasWaitingConsumer() {
+ return firstOfMode(false) != null;
+ }
+
+ /**
+ * Returns the number of elements in this queue. If this queue
+ * contains more than {@code Integer.MAX_VALUE} elements, returns
+ * {@code Integer.MAX_VALUE}.
+ *
+ * <p>Beware that, unlike in most collections, this method is
+ * <em>NOT</em> a constant-time operation. Because of the
+ * asynchronous nature of these queues, determining the current
+ * number of elements requires an O(n) traversal.
+ *
+ * @return the number of elements in this queue
+ */
+ public int size() {
+ return countOfMode(true);
+ }
+
+ public int getWaitingConsumerCount() {
+ return countOfMode(false);
+ }
+
+ /**
+ * Removes a single instance of the specified element from this queue,
+ * if it is present. More formally, removes an element {@code e} such
+ * that {@code o.equals(e)}, if this queue contains one or more such
+ * elements.
+ * Returns {@code true} if this queue contained the specified element
+ * (or equivalently, if this queue changed as a result of the call).
+ *
+ * @param o element to be removed from this queue, if present
+ * @return {@code true} if this queue changed as a result of the call
+ */
+ public boolean remove(Object o) {
+ return findAndRemove(o);
+ }
+
+ /**
+ * Returns {@code true} if this queue contains the specified element.
+ * More formally, returns {@code true} if and only if this queue contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
+ *
+ * @param o object to be checked for containment in this queue
+ * @return {@code true} if this queue contains the specified element
+ */
+ public boolean contains(Object o) {
+ if (o == null) return false;
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && o.equals(item))
+ return true;
+ }
+ else if (item == null)
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Always returns {@code Integer.MAX_VALUE} because a
+ * {@code LinkedTransferQueue} is not capacity constrained.
+ *
+ * @return {@code Integer.MAX_VALUE} (as specified by
+ * {@link java.util.concurrent.BlockingQueue#remainingCapacity()
+ * BlockingQueue.remainingCapacity})
+ */
+ public int remainingCapacity() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Saves the state to a stream (that is, serializes it).
+ *
+ * @serialData All of the elements (each an {@code E}) in
+ * the proper order, followed by a null
+ * @param s the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ for (E e : this)
+ s.writeObject(e);
+ // Use trailing null as sentinel
+ s.writeObject(null);
+ }
+
+ /**
+ * Reconstitutes the Queue instance from a stream (that is,
+ * deserializes it).
+ *
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ for (;;) {
+ @SuppressWarnings("unchecked") E item = (E) s.readObject();
+ if (item == null)
+ break;
+ else
+ offer(item);
+ }
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ private static final long sweepVotesOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = LinkedTransferQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ sweepVotesOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("sweepVotes"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java
new file mode 100644
index 0000000..25ff743
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/Phaser.java
@@ -0,0 +1,1135 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
+
+/**
+ * A reusable synchronization barrier, similar in functionality to
+ * {@link java.util.concurrent.CyclicBarrier CyclicBarrier} and
+ * {@link java.util.concurrent.CountDownLatch CountDownLatch}
+ * but supporting more flexible usage.
+ *
+ * <p> <b>Registration.</b> Unlike the case for other barriers, the
+ * number of parties <em>registered</em> to synchronize on a phaser
+ * may vary over time. Tasks may be registered at any time (using
+ * methods {@link #register}, {@link #bulkRegister}, or forms of
+ * constructors establishing initial numbers of parties), and
+ * optionally deregistered upon any arrival (using {@link
+ * #arriveAndDeregister}). As is the case with most basic
+ * synchronization constructs, registration and deregistration affect
+ * only internal counts; they do not establish any further internal
+ * bookkeeping, so tasks cannot query whether they are registered.
+ * (However, you can introduce such bookkeeping by subclassing this
+ * class.)
+ *
+ * <p> <b>Synchronization.</b> Like a {@code CyclicBarrier}, a {@code
+ * Phaser} may be repeatedly awaited. Method {@link
+ * #arriveAndAwaitAdvance} has effect analogous to {@link
+ * java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each
+ * generation of a phaser has an associated phase number. The phase
+ * number starts at zero, and advances when all parties arrive at the
+ * phaser, wrapping around to zero after reaching {@code
+ * Integer.MAX_VALUE}. The use of phase numbers enables independent
+ * control of actions upon arrival at a phaser and upon awaiting
+ * others, via two kinds of methods that may be invoked by any
+ * registered party:
+ *
+ * <ul>
+ *
+ * <li> <b>Arrival.</b> Methods {@link #arrive} and
+ * {@link #arriveAndDeregister} record arrival. These methods
+ * do not block, but return an associated <em>arrival phase
+ * number</em>; that is, the phase number of the phaser to which
+ * the arrival applied. When the final party for a given phase
+ * arrives, an optional action is performed and the phase
+ * advances. These actions are performed by the party
+ * triggering a phase advance, and are arranged by overriding
+ * method {@link #onAdvance(int, int)}, which also controls
+ * termination. Overriding this method is similar to, but more
+ * flexible than, providing a barrier action to a {@code
+ * CyclicBarrier}.
+ *
+ * <li> <b>Waiting.</b> Method {@link #awaitAdvance} requires an
+ * argument indicating an arrival phase number, and returns when
+ * the phaser advances to (or is already at) a different phase.
+ * Unlike similar constructions using {@code CyclicBarrier},
+ * method {@code awaitAdvance} continues to wait even if the
+ * waiting thread is interrupted. Interruptible and timeout
+ * versions are also available, but exceptions encountered while
+ * tasks wait interruptibly or with timeout do not change the
+ * state of the phaser. If necessary, you can perform any
+ * associated recovery within handlers of those exceptions,
+ * often after invoking {@code forceTermination}. Phasers may
+ * also be used by tasks executing in a {@link ForkJoinPool},
+ * which will ensure sufficient parallelism to execute tasks
+ * when others are blocked waiting for a phase to advance.
+ *
+ * </ul>
+ *
+ * <p> <b>Termination.</b> A phaser may enter a <em>termination</em>
+ * state, that may be checked using method {@link #isTerminated}. Upon
+ * termination, all synchronization methods immediately return without
+ * waiting for advance, as indicated by a negative return value.
+ * Similarly, attempts to register upon termination have no effect.
+ * Termination is triggered when an invocation of {@code onAdvance}
+ * returns {@code true}. The default implementation returns {@code
+ * true} if a deregistration has caused the number of registered
+ * parties to become zero. As illustrated below, when phasers control
+ * actions with a fixed number of iterations, it is often convenient
+ * to override this method to cause termination when the current phase
+ * number reaches a threshold. Method {@link #forceTermination} is
+ * also available to abruptly release waiting threads and allow them
+ * to terminate.
+ *
+ * <p> <b>Tiering.</b> Phasers may be <em>tiered</em> (i.e.,
+ * constructed in tree structures) to reduce contention. Phasers with
+ * large numbers of parties that would otherwise experience heavy
+ * synchronization contention costs may instead be set up so that
+ * groups of sub-phasers share a common parent. This may greatly
+ * increase throughput even though it incurs greater per-operation
+ * overhead.
+ *
+ * <p>In a tree of tiered phasers, registration and deregistration of
+ * child phasers with their parent are managed automatically.
+ * Whenever the number of registered parties of a child phaser becomes
+ * non-zero (as established in the {@link #Phaser(Phaser,int)}
+ * constructor, {@link #register}, or {@link #bulkRegister}), the
+ * child phaser is registered with its parent. Whenever the number of
+ * registered parties becomes zero as the result of an invocation of
+ * {@link #arriveAndDeregister}, the child phaser is deregistered
+ * from its parent.
+ *
+ * <p><b>Monitoring.</b> While synchronization methods may be invoked
+ * only by registered parties, the current state of a phaser may be
+ * monitored by any caller. At any given moment there are {@link
+ * #getRegisteredParties} parties in total, of which {@link
+ * #getArrivedParties} have arrived at the current phase ({@link
+ * #getPhase}). When the remaining ({@link #getUnarrivedParties})
+ * parties arrive, the phase advances. The values returned by these
+ * methods may reflect transient states and so are not in general
+ * useful for synchronization control. Method {@link #toString}
+ * returns snapshots of these state queries in a form convenient for
+ * informal monitoring.
+ *
+ * <p><b>Sample usages:</b>
+ *
+ * <p>A {@code Phaser} may be used instead of a {@code CountDownLatch}
+ * to control a one-shot action serving a variable number of parties.
+ * The typical idiom is for the method setting this up to first
+ * register, then start the actions, then deregister, as in:
+ *
+ * <pre> {@code
+ * void runTasks(List<Runnable> tasks) {
+ * final Phaser phaser = new Phaser(1); // "1" to register self
+ * // create and start threads
+ * for (final Runnable task : tasks) {
+ * phaser.register();
+ * new Thread() {
+ * public void run() {
+ * phaser.arriveAndAwaitAdvance(); // await all creation
+ * task.run();
+ * }
+ * }.start();
+ * }
+ *
+ * // allow threads to start and deregister self
+ * phaser.arriveAndDeregister();
+ * }}</pre>
+ *
+ * <p>One way to cause a set of threads to repeatedly perform actions
+ * for a given number of iterations is to override {@code onAdvance}:
+ *
+ * <pre> {@code
+ * void startTasks(List<Runnable> tasks, final int iterations) {
+ * final Phaser phaser = new Phaser() {
+ * protected boolean onAdvance(int phase, int registeredParties) {
+ * return phase >= iterations || registeredParties == 0;
+ * }
+ * };
+ * phaser.register();
+ * for (final Runnable task : tasks) {
+ * phaser.register();
+ * new Thread() {
+ * public void run() {
+ * do {
+ * task.run();
+ * phaser.arriveAndAwaitAdvance();
+ * } while (!phaser.isTerminated());
+ * }
+ * }.start();
+ * }
+ * phaser.arriveAndDeregister(); // deregister self, don't wait
+ * }}</pre>
+ *
+ * If the main task must later await termination, it
+ * may re-register and then execute a similar loop:
+ * <pre> {@code
+ * // ...
+ * phaser.register();
+ * while (!phaser.isTerminated())
+ * phaser.arriveAndAwaitAdvance();}</pre>
+ *
+ * <p>Related constructions may be used to await particular phase numbers
+ * in contexts where you are sure that the phase will never wrap around
+ * {@code Integer.MAX_VALUE}. For example:
+ *
+ * <pre> {@code
+ * void awaitPhase(Phaser phaser, int phase) {
+ * int p = phaser.register(); // assumes caller not already registered
+ * while (p < phase) {
+ * if (phaser.isTerminated())
+ * // ... deal with unexpected termination
+ * else
+ * p = phaser.arriveAndAwaitAdvance();
+ * }
+ * phaser.arriveAndDeregister();
+ * }}</pre>
+ *
+ *
+ * <p>To create a set of {@code n} tasks using a tree of phasers, you
+ * could use code of the following form, assuming a Task class with a
+ * constructor accepting a {@code Phaser} that it registers with upon
+ * construction. After invocation of {@code build(new Task[n], 0, n,
+ * new Phaser())}, these tasks could then be started, for example by
+ * submitting to a pool:
+ *
+ * <pre> {@code
+ * void build(Task[] tasks, int lo, int hi, Phaser ph) {
+ * if (hi - lo > TASKS_PER_PHASER) {
+ * for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
+ * int j = Math.min(i + TASKS_PER_PHASER, hi);
+ * build(tasks, i, j, new Phaser(ph));
+ * }
+ * } else {
+ * for (int i = lo; i < hi; ++i)
+ * tasks[i] = new Task(ph);
+ * // assumes new Task(ph) performs ph.register()
+ * }
+ * }}</pre>
+ *
+ * The best value of {@code TASKS_PER_PHASER} depends mainly on
+ * expected synchronization rates. A value as low as four may
+ * be appropriate for extremely small per-phase task bodies (thus
+ * high rates), or up to hundreds for extremely large ones.
+ *
+ * <p><b>Implementation notes</b>: This implementation restricts the
+ * maximum number of parties to 65535. Attempts to register additional
+ * parties result in {@code IllegalStateException}. However, you can and
+ * should create tiered phasers to accommodate arbitrarily large sets
+ * of participants.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class Phaser {
+ /*
+ * This class implements an extension of X10 "clocks". Thanks to
+ * Vijay Saraswat for the idea, and to Vivek Sarkar for
+ * enhancements to extend functionality.
+ */
+
+ /**
+ * Primary state representation, holding four bit-fields:
+ *
+ * unarrived -- the number of parties yet to hit barrier (bits 0-15)
+ * parties -- the number of parties to wait (bits 16-31)
+ * phase -- the generation of the barrier (bits 32-62)
+ * terminated -- set if barrier is terminated (bit 63 / sign)
+ *
+ * Except that a phaser with no registered parties is
+ * distinguished by the otherwise illegal state of having zero
+ * parties and one unarrived parties (encoded as EMPTY below).
+ *
+ * To efficiently maintain atomicity, these values are packed into
+ * a single (atomic) long. Good performance relies on keeping
+ * state decoding and encoding simple, and keeping race windows
+ * short.
+ *
+ * All state updates are performed via CAS except initial
+ * registration of a sub-phaser (i.e., one with a non-null
+ * parent). In this (relatively rare) case, we use built-in
+ * synchronization to lock while first registering with its
+ * parent.
+ *
+ * The phase of a subphaser is allowed to lag that of its
+ * ancestors until it is actually accessed -- see method
+ * reconcileState.
+ */
+ private volatile long state;
+
+ private static final int MAX_PARTIES = 0xffff;
+ private static final int MAX_PHASE = Integer.MAX_VALUE;
+ private static final int PARTIES_SHIFT = 16;
+ private static final int PHASE_SHIFT = 32;
+ private static final int UNARRIVED_MASK = 0xffff; // to mask ints
+ private static final long PARTIES_MASK = 0xffff0000L; // to mask longs
+ private static final long COUNTS_MASK = 0xffffffffL;
+ private static final long TERMINATION_BIT = 1L << 63;
+
+ // some special values
+ private static final int ONE_ARRIVAL = 1;
+ private static final int ONE_PARTY = 1 << PARTIES_SHIFT;
+ private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
+ private static final int EMPTY = 1;
+
+ // The following unpacking methods are usually manually inlined
+
+ private static int unarrivedOf(long s) {
+ int counts = (int)s;
+ return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ }
+
+ private static int partiesOf(long s) {
+ return (int)s >>> PARTIES_SHIFT;
+ }
+
+ private static int phaseOf(long s) {
+ return (int)(s >>> PHASE_SHIFT);
+ }
+
+ private static int arrivedOf(long s) {
+ int counts = (int)s;
+ return (counts == EMPTY) ? 0 :
+ (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK);
+ }
+
+ /**
+ * The parent of this phaser, or null if none
+ */
+ private final Phaser parent;
+
+ /**
+ * The root of phaser tree. Equals this if not in a tree.
+ */
+ private final Phaser root;
+
+ /**
+ * Heads of Treiber stacks for waiting threads. To eliminate
+ * contention when releasing some threads while adding others, we
+ * use two of them, alternating across even and odd phases.
+ * Subphasers share queues with root to speed up releases.
+ */
+ private final AtomicReference<QNode> evenQ;
+ private final AtomicReference<QNode> oddQ;
+
+ private AtomicReference<QNode> queueFor(int phase) {
+ return ((phase & 1) == 0) ? evenQ : oddQ;
+ }
+
+ /**
+ * Returns message string for bounds exceptions on arrival.
+ */
+ private String badArrive(long s) {
+ return "Attempted arrival of unregistered party for " +
+ stateToString(s);
+ }
+
+ /**
+ * Returns message string for bounds exceptions on registration.
+ */
+ private String badRegister(long s) {
+ return "Attempt to register more than " +
+ MAX_PARTIES + " parties for " + stateToString(s);
+ }
+
+ /**
+ * Main implementation for methods arrive and arriveAndDeregister.
+ * Manually tuned to speed up and minimize race windows for the
+ * common case of just decrementing unarrived field.
+ *
+ * @param adjust value to subtract from state;
+ * ONE_ARRIVAL for arrive,
+ * ONE_DEREGISTER for arriveAndDeregister
+ */
+ private int doArrive(int adjust) {
+ final Phaser root = this.root;
+ for (;;) {
+ long s = (root == this) ? state : reconcileState();
+ int phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ int counts = (int)s;
+ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ if (unarrived <= 0)
+ throw new IllegalStateException(badArrive(s));
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {
+ if (unarrived == 1) {
+ long n = s & PARTIES_MASK; // base of next state
+ int nextUnarrived = (int)n >>> PARTIES_SHIFT;
+ if (root == this) {
+ if (onAdvance(phase, nextUnarrived))
+ n |= TERMINATION_BIT;
+ else if (nextUnarrived == 0)
+ n |= EMPTY;
+ else
+ n |= nextUnarrived;
+ int nextPhase = (phase + 1) & MAX_PHASE;
+ n |= (long)nextPhase << PHASE_SHIFT;
+ UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
+ releaseWaiters(phase);
+ }
+ else if (nextUnarrived == 0) { // propagate deregistration
+ phase = parent.doArrive(ONE_DEREGISTER);
+ UNSAFE.compareAndSwapLong(this, stateOffset,
+ s, s | EMPTY);
+ }
+ else
+ phase = parent.doArrive(ONE_ARRIVAL);
+ }
+ return phase;
+ }
+ }
+ }
+
+ /**
+ * Implementation of register, bulkRegister
+ *
+ * @param registrations number to add to both parties and
+ * unarrived fields. Must be greater than zero.
+ */
+ private int doRegister(int registrations) {
+ // adjustment to state
+ long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
+ final Phaser parent = this.parent;
+ int phase;
+ for (;;) {
+ long s = (parent == null) ? state : reconcileState();
+ int counts = (int)s;
+ int parties = counts >>> PARTIES_SHIFT;
+ int unarrived = counts & UNARRIVED_MASK;
+ if (registrations > MAX_PARTIES - parties)
+ throw new IllegalStateException(badRegister(s));
+ phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ break;
+ if (counts != EMPTY) { // not 1st registration
+ if (parent == null || reconcileState() == s) {
+ if (unarrived == 0) // wait out advance
+ root.internalAwaitAdvance(phase, null);
+ else if (UNSAFE.compareAndSwapLong(this, stateOffset,
+ s, s + adjust))
+ break;
+ }
+ }
+ else if (parent == null) { // 1st root registration
+ long next = ((long)phase << PHASE_SHIFT) | adjust;
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
+ break;
+ }
+ else {
+ synchronized (this) { // 1st sub registration
+ if (state == s) { // recheck under lock
+ phase = parent.doRegister(1);
+ if (phase < 0)
+ break;
+ // finish registration whenever parent registration
+ // succeeded, even when racing with termination,
+ // since these are part of the same "transaction".
+ while (!UNSAFE.compareAndSwapLong
+ (this, stateOffset, s,
+ ((long)phase << PHASE_SHIFT) | adjust)) {
+ s = state;
+ phase = (int)(root.state >>> PHASE_SHIFT);
+ // assert (int)s == EMPTY;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return phase;
+ }
+
+ /**
+ * Resolves lagged phase propagation from root if necessary.
+ * Reconciliation normally occurs when root has advanced but
+ * subphasers have not yet done so, in which case they must finish
+ * their own advance by setting unarrived to parties (or if
+ * parties is zero, resetting to unregistered EMPTY state).
+ *
+ * @return reconciled state
+ */
+ private long reconcileState() {
+ final Phaser root = this.root;
+ long s = state;
+ if (root != this) {
+ int phase, p;
+ // CAS to root phase with current parties, tripping unarrived
+ while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
+ (int)(s >>> PHASE_SHIFT) &&
+ !UNSAFE.compareAndSwapLong
+ (this, stateOffset, s,
+ s = (((long)phase << PHASE_SHIFT) |
+ ((phase < 0) ? (s & COUNTS_MASK) :
+ (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
+ ((s & PARTIES_MASK) | p))))))
+ s = state;
+ }
+ return s;
+ }
+
+ /**
+ * Creates a new phaser with no initially registered parties, no
+ * parent, and initial phase number 0. Any thread using this
+ * phaser will need to first register for it.
+ */
+ public Phaser() {
+ this(null, 0);
+ }
+
+ /**
+ * Creates a new phaser with the given number of registered
+ * unarrived parties, no parent, and initial phase number 0.
+ *
+ * @param parties the number of parties required to advance to the
+ * next phase
+ * @throws IllegalArgumentException if parties less than zero
+ * or greater than the maximum number of parties supported
+ */
+ public Phaser(int parties) {
+ this(null, parties);
+ }
+
+ /**
+ * Equivalent to {@link #Phaser(Phaser, int) Phaser(parent, 0)}.
+ *
+ * @param parent the parent phaser
+ */
+ public Phaser(Phaser parent) {
+ this(parent, 0);
+ }
+
+ /**
+ * Creates a new phaser with the given parent and number of
+ * registered unarrived parties. When the given parent is non-null
+ * and the given number of parties is greater than zero, this
+ * child phaser is registered with its parent.
+ *
+ * @param parent the parent phaser
+ * @param parties the number of parties required to advance to the
+ * next phase
+ * @throws IllegalArgumentException if parties less than zero
+ * or greater than the maximum number of parties supported
+ */
+ public Phaser(Phaser parent, int parties) {
+ if (parties >>> PARTIES_SHIFT != 0)
+ throw new IllegalArgumentException("Illegal number of parties");
+ int phase = 0;
+ this.parent = parent;
+ if (parent != null) {
+ final Phaser root = parent.root;
+ this.root = root;
+ this.evenQ = root.evenQ;
+ this.oddQ = root.oddQ;
+ if (parties != 0)
+ phase = parent.doRegister(1);
+ }
+ else {
+ this.root = this;
+ this.evenQ = new AtomicReference<QNode>();
+ this.oddQ = new AtomicReference<QNode>();
+ }
+ this.state = (parties == 0) ? (long)EMPTY :
+ ((long)phase << PHASE_SHIFT) |
+ ((long)parties << PARTIES_SHIFT) |
+ ((long)parties);
+ }
+
+ /**
+ * Adds a new unarrived party to this phaser. If an ongoing
+ * invocation of {@link #onAdvance} is in progress, this method
+ * may await its completion before returning. If this phaser has
+ * a parent, and this phaser previously had no registered parties,
+ * this child phaser is also registered with its parent. If
+ * this phaser is terminated, the attempt to register has
+ * no effect, and a negative value is returned.
+ *
+ * @return the arrival phase number to which this registration
+ * applied. If this value is negative, then this phaser has
+ * terminated, in which case registration has no effect.
+ * @throws IllegalStateException if attempting to register more
+ * than the maximum supported number of parties
+ */
+ public int register() {
+ return doRegister(1);
+ }
+
+ /**
+ * Adds the given number of new unarrived parties to this phaser.
+ * If an ongoing invocation of {@link #onAdvance} is in progress,
+ * this method may await its completion before returning. If this
+ * phaser has a parent, and the given number of parties is greater
+ * than zero, and this phaser previously had no registered
+ * parties, this child phaser is also registered with its parent.
+ * If this phaser is terminated, the attempt to register has no
+ * effect, and a negative value is returned.
+ *
+ * @param parties the number of additional parties required to
+ * advance to the next phase
+ * @return the arrival phase number to which this registration
+ * applied. If this value is negative, then this phaser has
+ * terminated, in which case registration has no effect.
+ * @throws IllegalStateException if attempting to register more
+ * than the maximum supported number of parties
+ * @throws IllegalArgumentException if {@code parties < 0}
+ */
+ public int bulkRegister(int parties) {
+ if (parties < 0)
+ throw new IllegalArgumentException();
+ if (parties == 0)
+ return getPhase();
+ return doRegister(parties);
+ }
+
+ /**
+ * Arrives at this phaser, without waiting for others to arrive.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or a negative value if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of unarrived parties would become negative
+ */
+ public int arrive() {
+ return doArrive(ONE_ARRIVAL);
+ }
+
+ /**
+ * Arrives at this phaser and deregisters from it without waiting
+ * for others to arrive. Deregistration reduces the number of
+ * parties required to advance in future phases. If this phaser
+ * has a parent, and deregistration causes this phaser to have
+ * zero parties, this phaser is also deregistered from its parent.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or a negative value if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of registered or unarrived parties would become negative
+ */
+ public int arriveAndDeregister() {
+ return doArrive(ONE_DEREGISTER);
+ }
+
+ /**
+ * Arrives at this phaser and awaits others. Equivalent in effect
+ * to {@code awaitAdvance(arrive())}. If you need to await with
+ * interruption or timeout, you can arrange this with an analogous
+ * construction using one of the other forms of the {@code
+ * awaitAdvance} method. If instead you need to deregister upon
+ * arrival, use {@code awaitAdvance(arriveAndDeregister())}.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or the (negative)
+ * {@linkplain #getPhase() current phase} if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of unarrived parties would become negative
+ */
+ public int arriveAndAwaitAdvance() {
+ // Specialization of doArrive+awaitAdvance eliminating some reads/paths
+ final Phaser root = this.root;
+ for (;;) {
+ long s = (root == this) ? state : reconcileState();
+ int phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ int counts = (int)s;
+ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ if (unarrived <= 0)
+ throw new IllegalStateException(badArrive(s));
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
+ s -= ONE_ARRIVAL)) {
+ if (unarrived > 1)
+ return root.internalAwaitAdvance(phase, null);
+ if (root != this)
+ return parent.arriveAndAwaitAdvance();
+ long n = s & PARTIES_MASK; // base of next state
+ int nextUnarrived = (int)n >>> PARTIES_SHIFT;
+ if (onAdvance(phase, nextUnarrived))
+ n |= TERMINATION_BIT;
+ else if (nextUnarrived == 0)
+ n |= EMPTY;
+ else
+ n |= nextUnarrived;
+ int nextPhase = (phase + 1) & MAX_PHASE;
+ n |= (long)nextPhase << PHASE_SHIFT;
+ if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
+ return (int)(state >>> PHASE_SHIFT); // terminated
+ releaseWaiters(phase);
+ return nextPhase;
+ }
+ }
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value, returning immediately if the current phase is not equal
+ * to the given phase value or this phaser is terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ */
+ public int awaitAdvance(int phase) {
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase)
+ return root.internalAwaitAdvance(phase, null);
+ return p;
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value, throwing {@code InterruptedException} if interrupted
+ * while waiting, or returning immediately if the current phase is
+ * not equal to the given phase value or this phaser is
+ * terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ * @throws InterruptedException if thread interrupted while waiting
+ */
+ public int awaitAdvanceInterruptibly(int phase)
+ throws InterruptedException {
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase) {
+ QNode node = new QNode(this, phase, true, false, 0L);
+ p = root.internalAwaitAdvance(phase, node);
+ if (node.wasInterrupted)
+ throw new InterruptedException();
+ }
+ return p;
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value or the given timeout to elapse, throwing {@code
+ * InterruptedException} if interrupted while waiting, or
+ * returning immediately if the current phase is not equal to the
+ * given phase value or this phaser is terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @param timeout how long to wait before giving up, in units of
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ * @throws InterruptedException if thread interrupted while waiting
+ * @throws TimeoutException if timed out while waiting
+ */
+ public int awaitAdvanceInterruptibly(int phase,
+ long timeout, TimeUnit unit)
+ throws InterruptedException, TimeoutException {
+ long nanos = unit.toNanos(timeout);
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase) {
+ QNode node = new QNode(this, phase, true, true, nanos);
+ p = root.internalAwaitAdvance(phase, node);
+ if (node.wasInterrupted)
+ throw new InterruptedException();
+ else if (p == phase)
+ throw new TimeoutException();
+ }
+ return p;
+ }
+
+ /**
+ * Forces this phaser to enter termination state. Counts of
+ * registered parties are unaffected. If this phaser is a member
+ * of a tiered set of phasers, then all of the phasers in the set
+ * are terminated. If this phaser is already terminated, this
+ * method has no effect. This method may be useful for
+ * coordinating recovery after one or more tasks encounter
+ * unexpected exceptions.
+ */
+ public void forceTermination() {
+ // Only need to change root state
+ final Phaser root = this.root;
+ long s;
+ while ((s = root.state) >= 0) {
+ if (UNSAFE.compareAndSwapLong(root, stateOffset,
+ s, s | TERMINATION_BIT)) {
+ // signal all threads
+ releaseWaiters(0); // Waiters on evenQ
+ releaseWaiters(1); // Waiters on oddQ
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the current phase number. The maximum phase number is
+ * {@code Integer.MAX_VALUE}, after which it restarts at
+ * zero. Upon termination, the phase number is negative,
+ * in which case the prevailing phase prior to termination
+ * may be obtained via {@code getPhase() + Integer.MIN_VALUE}.
+ *
+ * @return the phase number, or a negative value if terminated
+ */
+ public final int getPhase() {
+ return (int)(root.state >>> PHASE_SHIFT);
+ }
+
+ /**
+ * Returns the number of parties registered at this phaser.
+ *
+ * @return the number of parties
+ */
+ public int getRegisteredParties() {
+ return partiesOf(state);
+ }
+
+ /**
+ * Returns the number of registered parties that have arrived at
+ * the current phase of this phaser. If this phaser has terminated,
+ * the returned value is meaningless and arbitrary.
+ *
+ * @return the number of arrived parties
+ */
+ public int getArrivedParties() {
+ return arrivedOf(reconcileState());
+ }
+
+ /**
+ * Returns the number of registered parties that have not yet
+ * arrived at the current phase of this phaser. If this phaser has
+ * terminated, the returned value is meaningless and arbitrary.
+ *
+ * @return the number of unarrived parties
+ */
+ public int getUnarrivedParties() {
+ return unarrivedOf(reconcileState());
+ }
+
+ /**
+ * Returns the parent of this phaser, or {@code null} if none.
+ *
+ * @return the parent of this phaser, or {@code null} if none
+ */
+ public Phaser getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the root ancestor of this phaser, which is the same as
+ * this phaser if it has no parent.
+ *
+ * @return the root ancestor of this phaser
+ */
+ public Phaser getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns {@code true} if this phaser has been terminated.
+ *
+ * @return {@code true} if this phaser has been terminated
+ */
+ public boolean isTerminated() {
+ return root.state < 0L;
+ }
+
+ /**
+ * Overridable method to perform an action upon impending phase
+ * advance, and to control termination. This method is invoked
+ * upon arrival of the party advancing this phaser (when all other
+ * waiting parties are dormant). If this method returns {@code
+ * true}, this phaser will be set to a final termination state
+ * upon advance, and subsequent calls to {@link #isTerminated}
+ * will return true. Any (unchecked) Exception or Error thrown by
+ * an invocation of this method is propagated to the party
+ * attempting to advance this phaser, in which case no advance
+ * occurs.
+ *
+ * <p>The arguments to this method provide the state of the phaser
+ * prevailing for the current transition. The effects of invoking
+ * arrival, registration, and waiting methods on this phaser from
+ * within {@code onAdvance} are unspecified and should not be
+ * relied on.
+ *
+ * <p>If this phaser is a member of a tiered set of phasers, then
+ * {@code onAdvance} is invoked only for its root phaser on each
+ * advance.
+ *
+ * <p>To support the most common use cases, the default
+ * implementation of this method returns {@code true} when the
+ * number of registered parties has become zero as the result of a
+ * party invoking {@code arriveAndDeregister}. You can disable
+ * this behavior, thus enabling continuation upon future
+ * registrations, by overriding this method to always return
+ * {@code false}:
+ *
+ * <pre> {@code
+ * Phaser phaser = new Phaser() {
+ * protected boolean onAdvance(int phase, int parties) { return false; }
+ * }}</pre>
+ *
+ * @param phase the current phase number on entry to this method,
+ * before this phaser is advanced
+ * @param registeredParties the current number of registered parties
+ * @return {@code true} if this phaser should terminate
+ */
+ protected boolean onAdvance(int phase, int registeredParties) {
+ return registeredParties == 0;
+ }
+
+ /**
+ * Returns a string identifying this phaser, as well as its
+ * state. The state, in brackets, includes the String {@code
+ * "phase = "} followed by the phase number, {@code "parties = "}
+ * followed by the number of registered parties, and {@code
+ * "arrived = "} followed by the number of arrived parties.
+ *
+ * @return a string identifying this phaser, as well as its state
+ */
+ public String toString() {
+ return stateToString(reconcileState());
+ }
+
+ /**
+ * Implementation of toString and string-based error messages
+ */
+ private String stateToString(long s) {
+ return super.toString() +
+ "[phase = " + phaseOf(s) +
+ " parties = " + partiesOf(s) +
+ " arrived = " + arrivedOf(s) + "]";
+ }
+
+ // Waiting mechanics
+
+ /**
+ * Removes and signals threads from queue for phase.
+ */
+ private void releaseWaiters(int phase) {
+ QNode q; // first element of queue
+ Thread t; // its thread
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ while ((q = head.get()) != null &&
+ q.phase != (int)(root.state >>> PHASE_SHIFT)) {
+ if (head.compareAndSet(q, q.next) &&
+ (t = q.thread) != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ }
+ }
+
+ /**
+ * Variant of releaseWaiters that additionally tries to remove any
+ * nodes no longer waiting for advance due to timeout or
+ * interrupt. Currently, nodes are removed only if they are at
+ * head of queue, which suffices to reduce memory footprint in
+ * most usages.
+ *
+ * @return current phase on exit
+ */
+ private int abortWait(int phase) {
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ for (;;) {
+ Thread t;
+ QNode q = head.get();
+ int p = (int)(root.state >>> PHASE_SHIFT);
+ if (q == null || ((t = q.thread) != null && q.phase == p))
+ return p;
+ if (head.compareAndSet(q, q.next) && t != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ }
+ }
+
+ /** The number of CPUs, for spin control */
+ private static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /**
+ * The number of times to spin before blocking while waiting for
+ * advance, per arrival while waiting. On multiprocessors, fully
+ * blocking and waking up a large number of threads all at once is
+ * usually a very slow process, so we use rechargeable spins to
+ * avoid it when threads regularly arrive: When a thread in
+ * internalAwaitAdvance notices another arrival before blocking,
+ * and there appear to be enough CPUs available, it spins
+ * SPINS_PER_ARRIVAL more times before blocking. The value trades
+ * off good-citizenship vs big unnecessary slowdowns.
+ */
+ static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8;
+
+ /**
+ * Possibly blocks and waits for phase to advance unless aborted.
+ * Call only on root phaser.
+ *
+ * @param phase current phase
+ * @param node if non-null, the wait node to track interrupt and timeout;
+ * if null, denotes noninterruptible wait
+ * @return current phase
+ */
+ private int internalAwaitAdvance(int phase, QNode node) {
+ // assert root == this;
+ releaseWaiters(phase-1); // ensure old queue clean
+ boolean queued = false; // true when node is enqueued
+ int lastUnarrived = 0; // to increase spins upon change
+ int spins = SPINS_PER_ARRIVAL;
+ long s;
+ int p;
+ while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
+ if (node == null) { // spinning in noninterruptible mode
+ int unarrived = (int)s & UNARRIVED_MASK;
+ if (unarrived != lastUnarrived &&
+ (lastUnarrived = unarrived) < NCPU)
+ spins += SPINS_PER_ARRIVAL;
+ boolean interrupted = Thread.interrupted();
+ if (interrupted || --spins < 0) { // need node to record intr
+ node = new QNode(this, phase, false, false, 0L);
+ node.wasInterrupted = interrupted;
+ }
+ }
+ else if (node.isReleasable()) // done or aborted
+ break;
+ else if (!queued) { // push onto queue
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ QNode q = node.next = head.get();
+ if ((q == null || q.phase == phase) &&
+ (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
+ queued = head.compareAndSet(q, node);
+ }
+ else {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ node.wasInterrupted = true;
+ }
+ }
+ }
+
+ if (node != null) {
+ if (node.thread != null)
+ node.thread = null; // avoid need for unpark()
+ if (node.wasInterrupted && !node.interruptible)
+ Thread.currentThread().interrupt();
+ if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)
+ return abortWait(phase); // possibly clean up on abort
+ }
+ releaseWaiters(phase);
+ return p;
+ }
+
+ /**
+ * Wait nodes for Treiber stack representing wait queue
+ */
+ static final class QNode implements ForkJoinPool.ManagedBlocker {
+ final Phaser phaser;
+ final int phase;
+ final boolean interruptible;
+ final boolean timed;
+ boolean wasInterrupted;
+ long nanos;
+ long lastTime;
+ volatile Thread thread; // nulled to cancel wait
+ QNode next;
+
+ QNode(Phaser phaser, int phase, boolean interruptible,
+ boolean timed, long nanos) {
+ this.phaser = phaser;
+ this.phase = phase;
+ this.interruptible = interruptible;
+ this.nanos = nanos;
+ this.timed = timed;
+ this.lastTime = timed ? System.nanoTime() : 0L;
+ thread = Thread.currentThread();
+ }
+
+ public boolean isReleasable() {
+ if (thread == null)
+ return true;
+ if (phaser.getPhase() != phase) {
+ thread = null;
+ return true;
+ }
+ if (Thread.interrupted())
+ wasInterrupted = true;
+ if (wasInterrupted && interruptible) {
+ thread = null;
+ return true;
+ }
+ if (timed) {
+ if (nanos > 0L) {
+ long now = System.nanoTime();
+ nanos -= now - lastTime;
+ lastTime = now;
+ }
+ if (nanos <= 0L) {
+ thread = null;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean block() {
+ if (isReleasable())
+ return true;
+ else if (!timed)
+ LockSupport.park(this);
+ else if (nanos > 0)
+ LockSupport.parkNanos(this, nanos);
+ return isReleasable();
+ }
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long stateOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Phaser.class;
+ stateOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("state"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index cffbe64..26c72eb 100644
--- a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -81,7 +81,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* java.util.PriorityQueue operations within a lock, as was done
* in a previous version of this class. To maintain
* interoperability, a plain PriorityQueue is still used during
- * serialization, which maintains compatibility at the espense of
+ * serialization, which maintains compatibility at the expense of
* transiently doubling overhead.
*/
@@ -139,7 +139,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* to maintain compatibility with previous versions
* of this class. Non-null only during serialization/deserialization.
*/
- private PriorityQueue q;
+ private PriorityQueue<E> q;
/**
* Creates a {@code PriorityBlockingQueue} with the default
@@ -278,14 +278,13 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
/**
* Mechanics for poll(). Call only while holding lock.
*/
- private E extract() {
- E result;
+ private E dequeue() {
int n = size - 1;
if (n < 0)
- result = null;
+ return null;
else {
Object[] array = queue;
- result = (E) array[0];
+ E result = (E) array[0];
E x = (E) array[n];
array[n] = null;
Comparator<? super E> cmp = comparator;
@@ -294,8 +293,8 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
else
siftDownUsingComparator(0, x, array, n, cmp);
size = n;
+ return result;
}
- return result;
}
/**
@@ -312,6 +311,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* @param k the position to fill
* @param x the item to insert
* @param array the heap array
+ * @param n heap size
*/
private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
@@ -476,7 +476,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* @param timeout This parameter is ignored as the method never blocks
* @param unit This parameter is ignored as the method never blocks
* @return {@code true} (as specified by
- * {@link BlockingQueue#offer BlockingQueue.offer})
+ * {@link BlockingQueue#offer(Object,long,TimeUnit) BlockingQueue.offer})
* @throws ClassCastException if the specified element cannot be compared
* with elements currently in the priority queue according to the
* priority queue's ordering
@@ -489,13 +489,11 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
- E result;
try {
- result = extract();
+ return dequeue();
} finally {
lock.unlock();
}
- return result;
}
public E take() throws InterruptedException {
@@ -503,7 +501,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
lock.lockInterruptibly();
E result;
try {
- while ( (result = extract()) == null)
+ while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
@@ -517,7 +515,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
lock.lockInterruptibly();
E result;
try {
- while ( (result = extract()) == null && nanos > 0)
+ while ( (result = dequeue()) == null && nanos > 0)
nanos = notEmpty.awaitNanos(nanos);
} finally {
lock.unlock();
@@ -528,13 +526,11 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
- E result;
try {
- result = size > 0 ? (E) queue[0] : null;
+ return (size == 0) ? null : (E) queue[0];
} finally {
lock.unlock();
}
- return result;
}
/**
@@ -618,32 +614,28 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
- boolean removed = false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = indexOf(o);
- if (i != -1) {
- removeAt(i);
- removed = true;
- }
+ if (i == -1)
+ return false;
+ removeAt(i);
+ return true;
} finally {
lock.unlock();
}
- return removed;
}
-
/**
* Identity-based version for use in Itr.remove
*/
- private void removeEQ(Object o) {
+ void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] array = queue;
- int n = size;
- for (int i = 0; i < n; i++) {
+ for (int i = 0, n = size; i < n; i++) {
if (o == array[i]) {
removeAt(i);
break;
@@ -663,15 +655,13 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- int index;
final ReentrantLock lock = this.lock;
lock.lock();
try {
- index = indexOf(o);
+ return indexOf(o) != -1;
} finally {
lock.unlock();
}
- return index != -1;
}
/**
@@ -697,7 +687,6 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
-
public String toString() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -708,7 +697,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
StringBuilder sb = new StringBuilder();
sb.append('[');
for (int i = 0; i < n; ++i) {
- E e = (E)queue[i];
+ Object e = queue[i];
sb.append(e == this ? "(this Collection)" : e);
if (i != n - 1)
sb.append(',').append(' ');
@@ -726,23 +715,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- if (c == null)
- throw new NullPointerException();
- if (c == this)
- throw new IllegalArgumentException();
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int n = 0;
- E e;
- while ( (e = extract()) != null) {
- c.add(e);
- ++n;
- }
- return n;
- } finally {
- lock.unlock();
- }
+ return drainTo(c, Integer.MAX_VALUE);
}
/**
@@ -761,11 +734,10 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int n = 0;
- E e;
- while (n < maxElements && (e = extract()) != null) {
- c.add(e);
- ++n;
+ int n = Math.min(size, maxElements);
+ for (int i = 0; i < n; i++) {
+ c.add((E) queue[0]); // In this order, in case add() throws.
+ dequeue();
}
return n;
} finally {
@@ -813,8 +785,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -867,7 +838,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
*/
final class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
- int cursor; // index of next element to return;
+ int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
@@ -904,8 +875,8 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
throws java.io.IOException {
lock.lock();
try {
- int n = size; // avoid zero capacity argument
- q = new PriorityQueue<E>(n == 0 ? 1 : n, comparator);
+ // avoid zero capacity argument
+ q = new PriorityQueue<E>(Math.max(size, 1), comparator);
q.addAll(this);
s.defaultWriteObject();
} finally {
@@ -933,21 +904,16 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long allocationSpinLockOffset =
- objectFieldOffset(UNSAFE, "allocationSpinLock",
- PriorityBlockingQueue.class);
-
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long allocationSpinLockOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = PriorityBlockingQueue.class;
+ allocationSpinLockOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("allocationSpinLock"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
new file mode 100644
index 0000000..48066c9
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
@@ -0,0 +1,165 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A recursive resultless {@link ForkJoinTask}. This class
+ * establishes conventions to parameterize resultless actions as
+ * {@code Void} {@code ForkJoinTask}s. Because {@code null} is the
+ * only valid value of type {@code Void}, methods such as {@code join}
+ * always return {@code null} upon completion.
+ *
+ * <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin
+ * sort that sorts a given {@code long[]} array:
+ *
+ * <pre> {@code
+ * static class SortTask extends RecursiveAction {
+ * final long[] array; final int lo, hi;
+ * SortTask(long[] array, int lo, int hi) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * }
+ * SortTask(long[] array) { this(array, 0, array.length); }
+ * protected void compute() {
+ * if (hi - lo < THRESHOLD)
+ * sortSequentially(lo, hi);
+ * else {
+ * int mid = (lo + hi) >>> 1;
+ * invokeAll(new SortTask(array, lo, mid),
+ * new SortTask(array, mid, hi));
+ * merge(lo, mid, hi);
+ * }
+ * }
+ * // implementation details follow:
+ * final static int THRESHOLD = 1000;
+ * void sortSequentially(int lo, int hi) {
+ * Arrays.sort(array, lo, hi);
+ * }
+ * void merge(int lo, int mid, int hi) {
+ * long[] buf = Arrays.copyOfRange(array, lo, mid);
+ * for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+ * array[j] = (k == hi || buf[i] < array[k]) ?
+ * buf[i++] : array[k++];
+ * }
+ * }}</pre>
+ *
+ * You could then sort {@code anArray} by creating {@code new
+ * SortTask(anArray)} and invoking it in a ForkJoinPool. As a more
+ * concrete simple example, the following task increments each element
+ * of an array:
+ * <pre> {@code
+ * class IncrementTask extends RecursiveAction {
+ * final long[] array; final int lo, hi;
+ * IncrementTask(long[] array, int lo, int hi) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * }
+ * protected void compute() {
+ * if (hi - lo < THRESHOLD) {
+ * for (int i = lo; i < hi; ++i)
+ * array[i]++;
+ * }
+ * else {
+ * int mid = (lo + hi) >>> 1;
+ * invokeAll(new IncrementTask(array, lo, mid),
+ * new IncrementTask(array, mid, hi));
+ * }
+ * }
+ * }}</pre>
+ *
+ * <p>The following example illustrates some refinements and idioms
+ * that may lead to better performance: RecursiveActions need not be
+ * fully recursive, so long as they maintain the basic
+ * divide-and-conquer approach. Here is a class that sums the squares
+ * of each element of a double array, by subdividing out only the
+ * right-hand-sides of repeated divisions by two, and keeping track of
+ * them with a chain of {@code next} references. It uses a dynamic
+ * threshold based on method {@code getSurplusQueuedTaskCount}, but
+ * counterbalances potential excess partitioning by directly
+ * performing leaf actions on unstolen tasks rather than further
+ * subdividing.
+ *
+ * <pre> {@code
+ * double sumOfSquares(ForkJoinPool pool, double[] array) {
+ * int n = array.length;
+ * Applyer a = new Applyer(array, 0, n, null);
+ * pool.invoke(a);
+ * return a.result;
+ * }
+ *
+ * class Applyer extends RecursiveAction {
+ * final double[] array;
+ * final int lo, hi;
+ * double result;
+ * Applyer next; // keeps track of right-hand-side tasks
+ * Applyer(double[] array, int lo, int hi, Applyer next) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * this.next = next;
+ * }
+ *
+ * double atLeaf(int l, int h) {
+ * double sum = 0;
+ * for (int i = l; i < h; ++i) // perform leftmost base step
+ * sum += array[i] * array[i];
+ * return sum;
+ * }
+ *
+ * protected void compute() {
+ * int l = lo;
+ * int h = hi;
+ * Applyer right = null;
+ * while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
+ * int mid = (l + h) >>> 1;
+ * right = new Applyer(array, mid, h, right);
+ * right.fork();
+ * h = mid;
+ * }
+ * double sum = atLeaf(l, h);
+ * while (right != null) {
+ * if (right.tryUnfork()) // directly calculate if not stolen
+ * sum += right.atLeaf(right.lo, right.hi);
+ * else {
+ * right.join();
+ * sum += right.result;
+ * }
+ * right = right.next;
+ * }
+ * result = sum;
+ * }
+ * }}</pre>
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class RecursiveAction extends ForkJoinTask<Void> {
+ private static final long serialVersionUID = 5232453952276485070L;
+
+ /**
+ * The main computation performed by this task.
+ */
+ protected abstract void compute();
+
+ /**
+ * Always returns {@code null}.
+ *
+ * @return {@code null} always
+ */
+ public final Void getRawResult() { return null; }
+
+ /**
+ * Requires null completion value.
+ */
+ protected final void setRawResult(Void mustBeNull) { }
+
+ /**
+ * Implements execution conventions for RecursiveActions.
+ */
+ protected final boolean exec() {
+ compute();
+ return true;
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
new file mode 100644
index 0000000..5e17280
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
@@ -0,0 +1,69 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A recursive result-bearing {@link ForkJoinTask}.
+ *
+ * <p>For a classic example, here is a task computing Fibonacci numbers:
+ *
+ * <pre> {@code
+ * class Fibonacci extends RecursiveTask<Integer> {
+ * final int n;
+ * Fibonacci(int n) { this.n = n; }
+ * Integer compute() {
+ * if (n <= 1)
+ * return n;
+ * Fibonacci f1 = new Fibonacci(n - 1);
+ * f1.fork();
+ * Fibonacci f2 = new Fibonacci(n - 2);
+ * return f2.compute() + f1.join();
+ * }
+ * }}</pre>
+ *
+ * However, besides being a dumb way to compute Fibonacci functions
+ * (there is a simple fast linear algorithm that you'd use in
+ * practice), this is likely to perform poorly because the smallest
+ * subtasks are too small to be worthwhile splitting up. Instead, as
+ * is the case for nearly all fork/join applications, you'd pick some
+ * minimum granularity size (for example 10 here) for which you always
+ * sequentially solve rather than subdividing.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
+ private static final long serialVersionUID = 5232453952276485270L;
+
+ /**
+ * The result of the computation.
+ */
+ V result;
+
+ /**
+ * The main computation performed by this task.
+ */
+ protected abstract V compute();
+
+ public final V getRawResult() {
+ return result;
+ }
+
+ protected final void setRawResult(V value) {
+ result = value;
+ }
+
+ /**
+ * Implements execution conventions for RecursiveTask.
+ */
+ protected final boolean exec() {
+ result = compute();
+ return true;
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
index 30b043d..f0005d1 100644
--- a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
+++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -49,8 +49,8 @@ public class RejectedExecutionException extends RuntimeException {
/**
* Constructs a <tt>RejectedExecutionException</tt> with the
- * specified cause. The detail message is set to: <pre> (cause ==
- * null ? null : cause.toString())</pre> (which typically contains
+ * specified cause. The detail message is set to {@code (cause ==
+ * null ? null : cause.toString())} (which typically contains
* the class and detail message of <tt>cause</tt>).
*
* @param cause the cause (which is saved for later retrieval by the
diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
index 417a27c..8c000ea 100644
--- a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
+++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/RunnableFuture.java b/luni/src/main/java/java/util/concurrent/RunnableFuture.java
index d74211d..2d6d52c 100644
--- a/luni/src/main/java/java/util/concurrent/RunnableFuture.java
+++ b/luni/src/main/java/java/util/concurrent/RunnableFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
index 0e8cc32..fbb995c 100644
--- a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
+++ b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index 6cb4e27..71e57ed 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -1,12 +1,10 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.atomic.*;
-import java.util.*;
/**
* An {@link ExecutorService} that can schedule commands to run after a given
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
index 239d681..3745cb0 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index c2eaedf..e41f0c3 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -1,16 +1,19 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.atomic.*;
-import java.util.concurrent.locks.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
// BEGIN android-note
-// Omit class-level docs on setRemoveOnCancelPolicy()
+// omit class-level docs on setRemoveOnCancelPolicy()
+// removed security manager docs
// END android-note
/**
@@ -138,7 +141,7 @@ public class ScheduledThreadPoolExecutor
* Sequence number to break scheduling ties, and in turn to
* guarantee FIFO order among tied entries.
*/
- private static final AtomicLong sequencer = new AtomicLong(0);
+ private static final AtomicLong sequencer = new AtomicLong();
/**
* Returns current nanosecond time.
@@ -203,11 +206,11 @@ public class ScheduledThreadPoolExecutor
}
public long getDelay(TimeUnit unit) {
- return unit.convert(time - now(), TimeUnit.NANOSECONDS);
+ return unit.convert(time - now(), NANOSECONDS);
}
public int compareTo(Delayed other) {
- if (other == this) // compare zero ONLY if same object
+ if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
@@ -221,9 +224,9 @@ public class ScheduledThreadPoolExecutor
else
return 1;
}
- long d = (getDelay(TimeUnit.NANOSECONDS) -
- other.getDelay(TimeUnit.NANOSECONDS));
- return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
+ long diff = (getDelay(NANOSECONDS) -
+ other.getDelay(NANOSECONDS));
+ return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
/**
@@ -302,7 +305,7 @@ public class ScheduledThreadPoolExecutor
remove(task))
task.cancel(false);
else
- prestartCoreThread();
+ ensurePrestart();
}
}
@@ -318,7 +321,7 @@ public class ScheduledThreadPoolExecutor
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
- prestartCoreThread();
+ ensurePrestart();
}
}
@@ -396,7 +399,7 @@ public class ScheduledThreadPoolExecutor
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
@@ -413,7 +416,7 @@ public class ScheduledThreadPoolExecutor
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
@@ -430,7 +433,7 @@ public class ScheduledThreadPoolExecutor
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
@@ -451,7 +454,7 @@ public class ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
@@ -480,7 +483,7 @@ public class ScheduledThreadPoolExecutor
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
- long headDelay = head.getDelay(TimeUnit.NANOSECONDS);
+ long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
@@ -588,7 +591,7 @@ public class ScheduledThreadPoolExecutor
* @throws NullPointerException {@inheritDoc}
*/
public void execute(Runnable command) {
- schedule(command, 0, TimeUnit.NANOSECONDS);
+ schedule(command, 0, NANOSECONDS);
}
// Override AbstractExecutorService methods
@@ -598,7 +601,7 @@ public class ScheduledThreadPoolExecutor
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
- return schedule(task, 0, TimeUnit.NANOSECONDS);
+ return schedule(task, 0, NANOSECONDS);
}
/**
@@ -606,8 +609,7 @@ public class ScheduledThreadPoolExecutor
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
- return schedule(Executors.callable(task, result),
- 0, TimeUnit.NANOSECONDS);
+ return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}
/**
@@ -615,7 +617,7 @@ public class ScheduledThreadPoolExecutor
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
- return schedule(task, 0, TimeUnit.NANOSECONDS);
+ return schedule(task, 0, NANOSECONDS);
}
/**
@@ -690,8 +692,9 @@ public class ScheduledThreadPoolExecutor
* @param value if {@code true}, remove on cancellation, else don't
* @see #getRemoveOnCancelPolicy
* @since 1.7
+ * @hide
*/
- /*public*/ void setRemoveOnCancelPolicy(boolean value) { // android-changed
+ public void setRemoveOnCancelPolicy(boolean value) {
removeOnCancel = value;
}
@@ -704,8 +707,9 @@ public class ScheduledThreadPoolExecutor
* from the queue
* @see #setRemoveOnCancelPolicy
* @since 1.7
+ * @hide
*/
- /*public*/ boolean getRemoveOnCancelPolicy() { // android-changed
+ public boolean getRemoveOnCancelPolicy() {
return removeOnCancel;
}
@@ -724,8 +728,6 @@ public class ScheduledThreadPoolExecutor
* ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set
* {@code true}, future executions of existing periodic tasks will
* be cancelled.
- *
- * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
super.shutdown();
@@ -750,7 +752,6 @@ public class ScheduledThreadPoolExecutor
* including those tasks submitted using {@code execute},
* which are for scheduling purposes used as the basis of a
* zero-delay {@code ScheduledFuture}.
- * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
return super.shutdownNow();
@@ -803,8 +804,8 @@ public class ScheduledThreadPoolExecutor
*/
private static final int INITIAL_CAPACITY = 16;
- private RunnableScheduledFuture[] queue =
- new RunnableScheduledFuture[INITIAL_CAPACITY];
+ private RunnableScheduledFuture<?>[] queue =
+ new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
@@ -835,7 +836,7 @@ public class ScheduledThreadPoolExecutor
/**
* Set f's heapIndex if it is a ScheduledFutureTask.
*/
- private void setIndex(RunnableScheduledFuture f, int idx) {
+ private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask)f).heapIndex = idx;
}
@@ -844,10 +845,10 @@ public class ScheduledThreadPoolExecutor
* Sift element added at bottom up to its heap-ordered spot.
* Call only when holding lock.
*/
- private void siftUp(int k, RunnableScheduledFuture key) {
+ private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
int parent = (k - 1) >>> 1;
- RunnableScheduledFuture e = queue[parent];
+ RunnableScheduledFuture<?> e = queue[parent];
if (key.compareTo(e) >= 0)
break;
queue[k] = e;
@@ -862,11 +863,11 @@ public class ScheduledThreadPoolExecutor
* Sift element added at top down to its heap-ordered spot.
* Call only when holding lock.
*/
- private void siftDown(int k, RunnableScheduledFuture key) {
+ private void siftDown(int k, RunnableScheduledFuture<?> key) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
- RunnableScheduledFuture c = queue[child];
+ RunnableScheduledFuture<?> c = queue[child];
int right = child + 1;
if (right < size && c.compareTo(queue[right]) > 0)
c = queue[child = right];
@@ -931,7 +932,7 @@ public class ScheduledThreadPoolExecutor
setIndex(queue[i], -1);
int s = --size;
- RunnableScheduledFuture replacement = queue[s];
+ RunnableScheduledFuture<?> replacement = queue[s];
queue[s] = null;
if (s != i) {
siftDown(i, replacement);
@@ -962,7 +963,7 @@ public class ScheduledThreadPoolExecutor
return Integer.MAX_VALUE;
}
- public RunnableScheduledFuture peek() {
+ public RunnableScheduledFuture<?> peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -975,7 +976,7 @@ public class ScheduledThreadPoolExecutor
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
- RunnableScheduledFuture e = (RunnableScheduledFuture)x;
+ RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -1017,9 +1018,9 @@ public class ScheduledThreadPoolExecutor
* holding lock.
* @param f the task to remove and return
*/
- private RunnableScheduledFuture finishPoll(RunnableScheduledFuture f) {
+ private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
int s = --size;
- RunnableScheduledFuture x = queue[s];
+ RunnableScheduledFuture<?> x = queue[s];
queue[s] = null;
if (s != 0)
siftDown(0, x);
@@ -1027,12 +1028,12 @@ public class ScheduledThreadPoolExecutor
return f;
}
- public RunnableScheduledFuture poll() {
+ public RunnableScheduledFuture<?> poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first = queue[0];
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ RunnableScheduledFuture<?> first = queue[0];
+ if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return finishPoll(first);
@@ -1041,16 +1042,16 @@ public class ScheduledThreadPoolExecutor
}
}
- public RunnableScheduledFuture take() throws InterruptedException {
+ public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
- RunnableScheduledFuture first = queue[0];
+ RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
else if (leader != null)
@@ -1074,21 +1075,21 @@ public class ScheduledThreadPoolExecutor
}
}
- public RunnableScheduledFuture poll(long timeout, TimeUnit unit)
+ public RunnableScheduledFuture<?> poll(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
- RunnableScheduledFuture first = queue[0];
+ RunnableScheduledFuture<?> first = queue[0];
if (first == null) {
if (nanos <= 0)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
if (nanos <= 0)
@@ -1120,7 +1121,7 @@ public class ScheduledThreadPoolExecutor
lock.lock();
try {
for (int i = 0; i < size; i++) {
- RunnableScheduledFuture t = queue[i];
+ RunnableScheduledFuture<?> t = queue[i];
if (t != null) {
queue[i] = null;
setIndex(t, -1);
@@ -1133,14 +1134,14 @@ public class ScheduledThreadPoolExecutor
}
/**
- * Return and remove first element only if it is expired.
+ * Return first element only if it is expired.
* Used only by drainTo. Call only when holding lock.
*/
- private RunnableScheduledFuture pollExpired() {
- RunnableScheduledFuture first = queue[0];
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- return null;
- return finishPoll(first);
+ private RunnableScheduledFuture<?> peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ RunnableScheduledFuture<?> first = queue[0];
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
}
public int drainTo(Collection<? super Runnable> c) {
@@ -1151,10 +1152,11 @@ public class ScheduledThreadPoolExecutor
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first;
+ RunnableScheduledFuture<?> first;
int n = 0;
- while ((first = pollExpired()) != null) {
- c.add(first);
+ while ((first = peekExpired()) != null) {
+ c.add(first); // In this order, in case add() throws.
+ finishPoll(first);
++n;
}
return n;
@@ -1173,10 +1175,11 @@ public class ScheduledThreadPoolExecutor
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first;
+ RunnableScheduledFuture<?> first;
int n = 0;
- while (n < maxElements && (first = pollExpired()) != null) {
- c.add(first);
+ while (n < maxElements && (first = peekExpired()) != null) {
+ c.add(first); // In this order, in case add() throws.
+ finishPoll(first);
++n;
}
return n;
diff --git a/luni/src/main/java/java/util/concurrent/Semaphore.java b/luni/src/main/java/java/util/concurrent/Semaphore.java
index 62dea1a..bf2524c 100644
--- a/luni/src/main/java/java/util/concurrent/Semaphore.java
+++ b/luni/src/main/java/java/util/concurrent/Semaphore.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.*;
-import java.util.concurrent.atomic.*;
/**
* A counting semaphore. Conceptually, a semaphore maintains a set of
@@ -20,7 +19,7 @@ import java.util.concurrent.atomic.*;
* <p>Semaphores are often used to restrict the number of threads than can
* access some (physical or logical) resource. For example, here is
* a class that uses a semaphore to control access to a pool of items:
- * <pre>
+ * <pre> {@code
* class Pool {
* private static final int MAX_AVAILABLE = 100;
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
@@ -62,9 +61,7 @@ import java.util.concurrent.atomic.*;
* }
* return false;
* }
- *
- * }
- * </pre>
+ * }}</pre>
*
* <p>Before obtaining an item each thread must acquire a permit from
* the semaphore, guaranteeing that an item is available for use. When
diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 51d40c0..b05ae0a 100644
--- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -2,13 +2,12 @@
* Written by Doug Lea, Bill Scherer, and Michael Scott with
* assistance from members of JCP JSR-166 Expert Group and released to
* the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
import java.util.*;
-import libcore.util.EmptyArray;
// BEGIN android-note
// removed link to collections framework docs
@@ -250,12 +249,22 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", SNode.class);
- private static final long matchOffset =
- objectFieldOffset(UNSAFE, "match", SNode.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long matchOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = SNode.class;
+ matchOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("match"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** The head (top) of the stack */
@@ -393,7 +402,6 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*/
long lastTime = timed ? System.nanoTime() : 0;
Thread w = Thread.currentThread();
- SNode h = head;
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
@@ -469,10 +477,18 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", TransferStack.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = TransferStack.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** Dual Queue */
@@ -529,11 +545,22 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", QNode.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", QNode.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = QNode.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** Head of queue */
@@ -762,15 +789,24 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
}
- // unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", TransferQueue.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", TransferQueue.class);
- private static final long cleanMeOffset =
- objectFieldOffset(UNSAFE, "cleanMe", TransferQueue.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ private static final long cleanMeOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = TransferQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ cleanMeOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("cleanMe"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -998,8 +1034,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*
* @return an empty iterator
*/
+ @SuppressWarnings("unchecked")
public Iterator<E> iterator() {
- return Collections.<E>emptySet().iterator(); // android-changed
+ return (Iterator<E>) EmptyIterator.EMPTY_ITERATOR;
+ }
+
+ // Replicated from a previous version of Collections
+ private static class EmptyIterator<E> implements Iterator<E> {
+ static final EmptyIterator<Object> EMPTY_ITERATOR
+ = new EmptyIterator<Object>();
+
+ public boolean hasNext() { return false; }
+ public E next() { throw new NoSuchElementException(); }
+ public void remove() { throw new IllegalStateException(); }
}
/**
@@ -1007,7 +1054,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
* @return a zero-length array
*/
public Object[] toArray() {
- return EmptyArray.OBJECT; // android-changed
+ return new Object[0];
}
/**
@@ -1036,8 +1083,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- E e;
- while ( (e = poll()) != null) {
+ for (E e; (e = poll()) != null;) {
c.add(e);
++n;
}
@@ -1056,8 +1102,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- E e;
- while (n < maxElements && (e = poll()) != null) {
+ for (E e; n < maxElements && (e = poll()) != null;) {
c.add(e);
++n;
}
@@ -1084,7 +1129,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
private WaitQueue waitingConsumers;
/**
- * Save the state to a stream (that is, serialize it).
+ * Saves the state to a stream (that is, serializes it).
*
* @param s the stream
*/
diff --git a/luni/src/main/java/java/util/concurrent/ThreadFactory.java b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
index 2f0fb1a..d1a4eb6 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadFactory.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -13,13 +13,12 @@ package java.util.concurrent;
*
* <p>
* The simplest implementation of this interface is just:
- * <pre>
+ * <pre> {@code
* class SimpleThreadFactory implements ThreadFactory {
* public Thread newThread(Runnable r) {
* return new Thread(r);
* }
- * }
- * </pre>
+ * }}</pre>
*
* The {@link Executors#defaultThreadFactory} method provides a more
* useful simple implementation, that sets the created thread context
diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
new file mode 100644
index 0000000..a559321
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
@@ -0,0 +1,198 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.Random;
+
+/**
+ * A random number generator isolated to the current thread. Like the
+ * global {@link java.util.Random} generator used by the {@link
+ * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
+ * with an internally generated seed that may not otherwise be
+ * modified. When applicable, use of {@code ThreadLocalRandom} rather
+ * than shared {@code Random} objects in concurrent programs will
+ * typically encounter much less overhead and contention. Use of
+ * {@code ThreadLocalRandom} is particularly appropriate when multiple
+ * tasks (for example, each a {@link ForkJoinTask}) use random numbers
+ * in parallel in thread pools.
+ *
+ * <p>Usages of this class should typically be of the form:
+ * {@code ThreadLocalRandom.current().nextX(...)} (where
+ * {@code X} is {@code Int}, {@code Long}, etc).
+ * When all usages are of this form, it is never possible to
+ * accidently share a {@code ThreadLocalRandom} across multiple threads.
+ *
+ * <p>This class also provides additional commonly used bounded random
+ * generation methods.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ThreadLocalRandom extends Random {
+ // same constants as Random, but must be redeclared because private
+ private static final long multiplier = 0x5DEECE66DL;
+ private static final long addend = 0xBL;
+ private static final long mask = (1L << 48) - 1;
+
+ /**
+ * The random seed. We can't use super.seed.
+ */
+ private long rnd;
+
+ /**
+ * Initialization flag to permit calls to setSeed to succeed only
+ * while executing the Random constructor. We can't allow others
+ * since it would cause setting seed in one part of a program to
+ * unintentionally impact other usages by the thread.
+ */
+ boolean initialized;
+
+ // Padding to help avoid memory contention among seed updates in
+ // different TLRs in the common case that they are located near
+ // each other.
+ private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+
+ /**
+ * The actual ThreadLocal
+ */
+ private static final ThreadLocal<ThreadLocalRandom> localRandom =
+ new ThreadLocal<ThreadLocalRandom>() {
+ protected ThreadLocalRandom initialValue() {
+ return new ThreadLocalRandom();
+ }
+ };
+
+
+ /**
+ * Constructor called only by localRandom.initialValue.
+ */
+ ThreadLocalRandom() {
+ super();
+ initialized = true;
+ }
+
+ /**
+ * Returns the current thread's {@code ThreadLocalRandom}.
+ *
+ * @return the current thread's {@code ThreadLocalRandom}
+ */
+ public static ThreadLocalRandom current() {
+ return localRandom.get();
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}. Setting seeds in
+ * this generator is not supported.
+ *
+ * @throws UnsupportedOperationException always
+ */
+ public void setSeed(long seed) {
+ if (initialized)
+ throw new UnsupportedOperationException();
+ rnd = (seed ^ multiplier) & mask;
+ }
+
+ protected int next(int bits) {
+ rnd = (rnd * multiplier + addend) & mask;
+ return (int) (rnd >>> (48-bits));
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ * @return the next value
+ */
+ public int nextInt(int least, int bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextInt(bound - least) + least;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value
+ * between 0 (inclusive) and the specified value (exclusive).
+ *
+ * @param n the bound on the random number to be returned. Must be
+ * positive.
+ * @return the next value
+ * @throws IllegalArgumentException if n is not positive
+ */
+ public long nextLong(long n) {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+ // Divide n by two until small enough for nextInt. On each
+ // iteration (at most 31 of them but usually much less),
+ // randomly choose both whether to include high bit in result
+ // (offset) and whether to continue with the lower vs upper
+ // half (which makes a difference only if odd).
+ long offset = 0;
+ while (n >= Integer.MAX_VALUE) {
+ int bits = next(2);
+ long half = n >>> 1;
+ long nextn = ((bits & 2) == 0) ? half : n - half;
+ if ((bits & 1) == 0)
+ offset += n - nextn;
+ n = nextn;
+ }
+ return offset + nextInt((int) n);
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return the next value
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ */
+ public long nextLong(long least, long bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextLong(bound - least) + least;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code double} value
+ * between 0 (inclusive) and the specified value (exclusive).
+ *
+ * @param n the bound on the random number to be returned. Must be
+ * positive.
+ * @return the next value
+ * @throws IllegalArgumentException if n is not positive
+ */
+ public double nextDouble(double n) {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+ return nextDouble() * n;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return the next value
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ */
+ public double nextDouble(double least, double bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextDouble() * (bound - least) + least;
+ }
+
+ private static final long serialVersionUID = -5851777807851030925L;
+}
diff --git a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
index 6622af8..331e225 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -9,6 +9,10 @@ import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
import java.util.*;
+// BEGIN android-note
+// removed security manager docs
+// END android-note
+
/**
* An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
@@ -1311,8 +1315,6 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
- *
- * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
@@ -1342,8 +1344,6 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
- *
- * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
@@ -1512,6 +1512,18 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
+ * Same as prestartCoreThread except arranges that at least one
+ * thread is started even if corePoolSize is 0.
+ */
+ void ensurePrestart() {
+ int wc = workerCountOf(ctl.get());
+ if (wc < corePoolSize)
+ addWorker(null, true);
+ else if (wc == 0)
+ addWorker(null, false);
+ }
+
+ /**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed.
diff --git a/luni/src/main/java/java/util/concurrent/TimeUnit.java b/luni/src/main/java/java/util/concurrent/TimeUnit.java
index b2e3060..50f6ce0 100644
--- a/luni/src/main/java/java/util/concurrent/TimeUnit.java
+++ b/luni/src/main/java/java/util/concurrent/TimeUnit.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,14 +23,14 @@ package java.util.concurrent;
* the following code will timeout in 50 milliseconds if the {@link
* java.util.concurrent.locks.Lock lock} is not available:
*
- * <pre> Lock lock = ...;
- * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...}</pre>
+ *
* while this code will timeout in 50 seconds:
- * <pre>
- * Lock lock = ...;
- * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...}</pre>
*
* Note however, that there is no guarantee that a particular timeout
* implementation will be able to notice the passage of time at the
diff --git a/luni/src/main/java/java/util/concurrent/TimeoutException.java b/luni/src/main/java/java/util/concurrent/TimeoutException.java
index 8b84f28..83934f0 100644
--- a/luni/src/main/java/java/util/concurrent/TimeoutException.java
+++ b/luni/src/main/java/java/util/concurrent/TimeoutException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java
new file mode 100644
index 0000000..9cd5773
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java
@@ -0,0 +1,133 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+/**
+ * A {@link BlockingQueue} in which producers may wait for consumers
+ * to receive elements. A {@code TransferQueue} may be useful for
+ * example in message passing applications in which producers
+ * sometimes (using method {@link #transfer}) await receipt of
+ * elements by consumers invoking {@code take} or {@code poll}, while
+ * at other times enqueue elements (via method {@code put}) without
+ * waiting for receipt.
+ * {@linkplain #tryTransfer(Object) Non-blocking} and
+ * {@linkplain #tryTransfer(Object,long,TimeUnit) time-out} versions of
+ * {@code tryTransfer} are also available.
+ * A {@code TransferQueue} may also be queried, via {@link
+ * #hasWaitingConsumer}, whether there are any threads waiting for
+ * items, which is a converse analogy to a {@code peek} operation.
+ *
+ * <p>Like other blocking queues, a {@code TransferQueue} may be
+ * capacity bounded. If so, an attempted transfer operation may
+ * initially block waiting for available space, and/or subsequently
+ * block waiting for reception by a consumer. Note that in a queue
+ * with zero capacity, such as {@link SynchronousQueue}, {@code put}
+ * and {@code transfer} are effectively synonymous.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+public interface TransferQueue<E> extends BlockingQueue<E> {
+ /**
+ * Transfers the element to a waiting consumer immediately, if possible.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * otherwise returning {@code false} without enqueuing the element.
+ *
+ * @param e the element to transfer
+ * @return {@code true} if the element was transferred, else
+ * {@code false}
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ boolean tryTransfer(E e);
+
+ /**
+ * Transfers the element to a consumer, waiting if necessary to do so.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else waits until the element is received by a consumer.
+ *
+ * @param e the element to transfer
+ * @throws InterruptedException if interrupted while waiting,
+ * in which case the element is not left enqueued
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ void transfer(E e) throws InterruptedException;
+
+ /**
+ * Transfers the element to a consumer if it is possible to do so
+ * before the timeout elapses.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else waits until the element is received by a consumer,
+ * returning {@code false} if the specified wait time elapses
+ * before the element can be transferred.
+ *
+ * @param e the element to transfer
+ * @param timeout how long to wait before giving up, in units of
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return {@code true} if successful, or {@code false} if
+ * the specified waiting time elapses before completion,
+ * in which case the element is not left enqueued
+ * @throws InterruptedException if interrupted while waiting,
+ * in which case the element is not left enqueued
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ boolean tryTransfer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException;
+
+ /**
+ * Returns {@code true} if there is at least one consumer waiting
+ * to receive an element via {@link #take} or
+ * timed {@link #poll(long,TimeUnit) poll}.
+ * The return value represents a momentary state of affairs.
+ *
+ * @return {@code true} if there is at least one waiting consumer
+ */
+ boolean hasWaitingConsumer();
+
+ /**
+ * Returns an estimate of the number of consumers waiting to
+ * receive elements via {@link #take} or timed
+ * {@link #poll(long,TimeUnit) poll}. The return value is an
+ * approximation of a momentary state of affairs, that may be
+ * inaccurate if consumers have completed or given up waiting.
+ * The value may be useful for monitoring and heuristics, but
+ * not for synchronization control. Implementations of this
+ * method are likely to be noticeably slower than those for
+ * {@link #hasWaitingConsumer}.
+ *
+ * @return the number of consumers waiting to receive elements
+ */
+ int getWaitingConsumerCount();
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
index c774d21..d531f25 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -21,14 +21,14 @@ import sun.misc.Unsafe;
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
// setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicBoolean.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicBoolean.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
index 16dd568..e0a0018 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -24,14 +24,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicInteger.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
@@ -217,18 +217,33 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as an {@code int}.
+ */
public int intValue() {
return get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code long}
+ * after a widening primitive conversion.
+ */
public long longValue() {
return (long)get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code float}
+ * after a widening primitive conversion.
+ */
public float floatValue() {
return (float)get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code double}
+ * after a widening primitive conversion.
+ */
public double doubleValue() {
return (double)get();
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
index 6dcdfd0..804a51e 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
import sun.misc.Unsafe;
-import java.util.*;
/**
* An {@code int} array in which elements may be updated atomically.
@@ -19,7 +18,7 @@ import java.util.*;
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
index e8a0d57..c7ed158 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -236,24 +236,21 @@ public abstract class AtomicIntegerFieldUpdater<T> {
* Standard hotspot implementation using intrinsics
*/
private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
- // BEGIN android-changed
- caller = VMStack.getStackClass2();
- // END android-changed
+ caller = VMStack.getStackClass2(); // android-changed
modifiers = field.getModifiers();
// BEGIN android-removed
- // modifiers = field.getModifiers();
// sun.reflect.misc.ReflectUtil.ensureMemberAccess(
// caller, tclass, null, modifiers);
// sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
@@ -262,7 +259,7 @@ public abstract class AtomicIntegerFieldUpdater<T> {
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type");
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
index 12d6cd1..5e799f7 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -24,7 +24,7 @@ public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
// setup to use Unsafe.compareAndSwapLong for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
/**
@@ -42,10 +42,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
private static native boolean VMSupportsCS8();
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicLong.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicLong.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile long value;
@@ -231,18 +231,33 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
+ /**
+ * Returns the value of this {@code AtomicLong} as an {@code int}
+ * after a narrowing primitive conversion.
+ */
public int intValue() {
return (int)get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code long}.
+ */
public long longValue() {
return get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code float}
+ * after a widening primitive conversion.
+ */
public float floatValue() {
return (float)get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code double}
+ * after a widening primitive conversion.
+ */
public double doubleValue() {
return (double)get();
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
index 9e2d25f..22edb3f 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
import sun.misc.Unsafe;
-import java.util.*;
/**
* A {@code long} array in which elements may be updated atomically.
@@ -18,7 +17,7 @@ import java.util.*;
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(long[].class);
private static final int shift;
private final long[] array;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index 21ef748..748ae69 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -235,14 +235,14 @@ public abstract class AtomicLongFieldUpdater<T> {
}
private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
CASUpdater(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
@@ -257,7 +257,7 @@ public abstract class AtomicLongFieldUpdater<T> {
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
@@ -320,14 +320,14 @@ public abstract class AtomicLongFieldUpdater<T> {
private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
LockedUpdater(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
@@ -342,7 +342,7 @@ public abstract class AtomicLongFieldUpdater<T> {
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
index 63f46d6..eaf700c 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
@@ -1,13 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
-
/**
* An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
@@ -163,7 +161,7 @@ public class AtomicMarkableReference<V> {
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed
+ private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
index f041bbd..b21e9b6 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -18,14 +18,14 @@ import sun.misc.Unsafe;
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicReference.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicReference.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
index dbc5886..c47728d 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
@@ -1,12 +1,14 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
+
+import java.util.Arrays;
+import java.lang.reflect.Array;
import sun.misc.Unsafe;
-import java.util.*;
/**
* An array of object references in which elements may be updated
@@ -20,13 +22,23 @@ import java.util.*;
public class AtomicReferenceArray<E> implements java.io.Serializable {
private static final long serialVersionUID = -6209656149925076980L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
- private static final int base = unsafe.arrayBaseOffset(Object[].class);
+ private static final Unsafe unsafe;
+ private static final int base;
private static final int shift;
- private final Object[] array;
+ private static final long arrayFieldOffset;
+ private final Object[] array; // must have exact type Object[]
static {
- int scale = unsafe.arrayIndexScale(Object[].class);
+ int scale;
+ try {
+ unsafe = Unsafe.getUnsafe();
+ arrayFieldOffset = unsafe.objectFieldOffset
+ (AtomicReferenceArray.class.getDeclaredField("array"));
+ base = unsafe.arrayBaseOffset(Object[].class);
+ scale = unsafe.arrayIndexScale(Object[].class);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
@@ -45,7 +57,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
/**
* Creates a new AtomicReferenceArray of the given length, with all
- * elements initially zero.
+ * elements initially null.
*
* @param length the length of the array
*/
@@ -62,7 +74,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
*/
public AtomicReferenceArray(E[] array) {
// Visibility guaranteed by final field guarantees
- this.array = array.clone();
+ this.array = Arrays.copyOf(array, array.length, Object[].class);
}
/**
@@ -121,7 +133,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
public final E getAndSet(int i, E newValue) {
long offset = checkedByteOffset(i);
while (true) {
- E current = (E) getRaw(offset);
+ E current = getRaw(offset);
if (compareAndSetRaw(offset, current, newValue))
return current;
}
@@ -167,7 +179,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
* @return the String representation of the current values of array
*/
public String toString() {
- int iMax = array.length - 1;
+ int iMax = array.length - 1;
if (iMax == -1)
return "[]";
@@ -181,4 +193,20 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
}
}
+ /**
+ * Reconstitutes the instance from a stream (that is, deserializes it).
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException,
+ java.io.InvalidObjectException {
+ // Note: This must be changed if any additional fields are defined
+ Object a = s.readFields().get("array", null);
+ if (a == null || !a.getClass().isArray())
+ throw new java.io.InvalidObjectException("Not array type");
+ if (a.getClass() != Object[].class)
+ a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
+ unsafe.putObjectVolatile(this, arrayFieldOffset, a);
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
index 8b3da0b..d23d766 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
@@ -1,11 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
-import dalvik.system.VMStack;
+import dalvik.system.VMStack; // android-added
import sun.misc.Unsafe;
import java.lang.reflect.*;
@@ -155,7 +155,7 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
private final long offset;
private final Class<T> tclass;
private final Class<V> vclass;
- private final Class cclass;
+ private final Class<?> cclass;
/*
* Internal type checks within all update methods contain
@@ -173,8 +173,8 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
Class<V> vclass,
String fieldName) {
Field field = null;
- Class fieldClass = null;
- Class caller = null;
+ Class<?> fieldClass = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
index 2e826f2..a0cb492 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -162,7 +162,7 @@ public class AtomicStampedReference<V> {
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed
+ private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
new file mode 100644
index 0000000..7ecf45a
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
@@ -0,0 +1,540 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent.atomic;
+
+/**
+ * A set of methods providing fine-grained control over happens-before
+ * and synchronization order relations among reads and/or writes. The
+ * methods of this class are designed for use in uncommon situations
+ * where declaring variables {@code volatile} or {@code final}, using
+ * instances of atomic classes, using {@code synchronized} blocks or
+ * methods, or using other synchronization facilities are not possible
+ * or do not provide the desired control.
+ *
+ * <p><b>Memory Ordering.</b> There are three methods for controlling
+ * ordering relations among memory accesses (i.e., reads and
+ * writes). Method {@code orderWrites} is typically used to enforce
+ * order between two writes, and {@code orderAccesses} between a write
+ * and a read. Method {@code orderReads} is used to enforce order
+ * between two reads with respect to other {@code orderWrites} and/or
+ * {@code orderAccesses} invocations. The formally specified
+ * properties of these methods described below provide
+ * platform-independent guarantees that are honored by all levels of a
+ * platform (compilers, systems, processors). The use of these
+ * methods may result in the suppression of otherwise valid compiler
+ * transformations and optimizations that could visibly violate the
+ * specified orderings, and may or may not entail the use of
+ * processor-level "memory barrier" instructions.
+ *
+ * <p>Each ordering method accepts a {@code ref} argument, and
+ * controls ordering among accesses with respect to this reference.
+ * Invocations must be placed <em>between</em> accesses performed in
+ * expression evaluations and assignment statements to control the
+ * orderings of prior versus subsequent accesses appearing in program
+ * order. These methods also return their arguments to simplify
+ * correct usage in these contexts.
+ *
+ * <p>Usages of ordering methods almost always take one of the forms
+ * illustrated in the examples below. These idioms arrange some of
+ * the ordering properties associated with {@code volatile} and
+ * related language-based constructions, but without other
+ * compile-time and runtime benefits that make language-based
+ * constructions far better choices when they are applicable. Usages
+ * should be restricted to the control of strictly internal
+ * implementation matters inside a class or package, and must either
+ * avoid or document any consequent violations of ordering or safety
+ * properties expected by users of a class employing them.
+ *
+ * <p><b>Reachability.</b> Method {@code reachabilityFence}
+ * establishes an ordering for strong reachability (as defined in the
+ * {@link java.lang.ref} package specification) with respect to
+ * garbage collection. Method {@code reachabilityFence} differs from
+ * the others in that it controls relations that are otherwise only
+ * implicit in a program -- the reachability conditions triggering
+ * garbage collection. As illustrated in the sample usages below,
+ * this method is applicable only when reclamation may have visible
+ * effects, which is possible for objects with finalizers (see Section
+ * 12.6 of the Java Language Specification) that are implemented in
+ * ways that rely on ordering control for correctness.
+ *
+ * <p><b>Sample Usages</b>
+ *
+ * <p><b>Safe publication.</b> With care, method {@code orderWrites}
+ * may be used to obtain the memory safety effects of {@code final}
+ * for a field that cannot be declared as {@code final}, because its
+ * primary initialization cannot be performed in a constructor, in
+ * turn because it is used in a framework requiring that all classes
+ * have a no-argument constructor; as in:
+ *
+ * <pre> {@code
+ * class WidgetHolder {
+ * private Widget widget;
+ * public WidgetHolder() {}
+ * public static WidgetHolder newWidgetHolder(Params params) {
+ * WidgetHolder h = new WidgetHolder();
+ * h.widget = new Widget(params);
+ * return Fences.orderWrites(h);
+ * }
+ * }}</pre>
+ *
+ * Here, the invocation of {@code orderWrites} ensures that the
+ * effects of the widget assignment are ordered before those of any
+ * (unknown) subsequent stores of {@code h} in other variables that
+ * make {@code h} available for use by other objects. Initialization
+ * sequences using {@code orderWrites} require more care than those
+ * involving {@code final} fields. When {@code final} is not used,
+ * compilers cannot help you to ensure that the field is set correctly
+ * across all usages. You must fully initialize objects
+ * <em>before</em> the {@code orderWrites} invocation that makes
+ * references to them safe to assign to accessible variables. Further,
+ * initialization sequences must not internally "leak" the reference
+ * by using it as an argument to a callback method or adding it to a
+ * static data structure. If less constrained usages were required,
+ * it may be possible to cope using more extensive sets of fences, or
+ * as a normally better choice, using synchronization (locking).
+ * Conversely, if it were possible to do so, the best option would be
+ * to rewrite class {@code WidgetHolder} to use {@code final}.
+ *
+ * <p>An alternative approach is to place similar mechanics in the
+ * (sole) method that makes such objects available for use by others.
+ * Here is a stripped-down example illustrating the essentials. In
+ * practice, among other changes, you would use access methods instead
+ * of a public field.
+ *
+ * <pre> {@code
+ * class AnotherWidgetHolder {
+ * public Widget widget;
+ * void publish(Widget w) {
+ * this.widget = Fences.orderWrites(w);
+ * }
+ * // ...
+ * }}</pre>
+ *
+ * In this case, the {@code orderWrites} invocation occurs before the
+ * store making the object available. Correctness again relies on
+ * ensuring that there are no leaks prior to invoking this method, and
+ * that it really is the <em>only</em> means of accessing the
+ * published object. This approach is not often applicable --
+ * normally you would publish objects using a thread-safe collection
+ * that itself guarantees the expected ordering relations. However, it
+ * may come into play in the construction of such classes themselves.
+ *
+ * <p><b>Safely updating fields.</b> Outside of the initialization
+ * idioms illustrated above, Fence methods ordering writes must be
+ * paired with those ordering reads. To illustrate, suppose class
+ * {@code c} contains an accessible variable {@code data} that should
+ * have been declared as {@code volatile} but wasn't:
+ *
+ * <pre> {@code
+ * class C {
+ * Object data; // need volatile access but not volatile
+ * // ...
+ * }
+ *
+ * class App {
+ * Object getData(C c) {
+ * return Fences.orderReads(c).data;
+ * }
+ *
+ * void setData(C c) {
+ * Object newValue = ...;
+ * c.data = Fences.orderWrites(newValue);
+ * Fences.orderAccesses(c);
+ * }
+ * // ...
+ * }}</pre>
+ *
+ * Method {@code getData} provides an emulation of {@code volatile}
+ * reads of (non-long/double) fields by ensuring that the read of
+ * {@code c} obtained as an argument is ordered before subsequent
+ * reads using this reference, and then performs the read of its
+ * field. Method {@code setData} provides an emulation of volatile
+ * writes, ensuring that all other relevant writes have completed,
+ * then performing the assignment, and then ensuring that the write is
+ * ordered before any other access. These techniques may apply even
+ * when fields are not directly accessible, in which case calls to
+ * fence methods would surround calls to methods such as {@code
+ * c.getData()}. However, these techniques cannot be applied to
+ * {@code long} or {@code double} fields because reads and writes of
+ * fields of these types are not guaranteed to be
+ * atomic. Additionally, correctness may require that all accesses of
+ * such data use these kinds of wrapper methods, which you would need
+ * to manually ensure.
+ *
+ * <p>More generally, Fence methods can be used in this way to achieve
+ * the safety properties of {@code volatile}. However their use does
+ * not necessarily guarantee the full sequential consistency
+ * properties specified in the Java Language Specification chapter 17
+ * for programs using {@code volatile}. In particular, emulation using
+ * Fence methods is not guaranteed to maintain the property that
+ * {@code volatile} operations performed by different threads are
+ * observed in the same order by all observer threads.
+ *
+ * <p><b>Acquire/Release management of threadsafe objects</b>. It may
+ * be possible to use weaker conventions for volatile-like variables
+ * when they are used to keep track of objects that fully manage their
+ * own thread-safety and synchronization. Here, an acquiring read
+ * operation remains the same as a volatile-read, but a releasing
+ * write differs by virtue of not itself ensuring an ordering of its
+ * write with subsequent reads, because the required effects are
+ * already ensured by the referenced objects.
+ * For example:
+ *
+ * <pre> {@code
+ * class Item {
+ * synchronized f(); // ALL methods are synchronized
+ * // ...
+ * }
+ *
+ * class ItemHolder {
+ * private Item item;
+ * Item acquireItem() {
+ * return Fences.orderReads(item);
+ * }
+ *
+ * void releaseItem(Item x) {
+ * item = Fences.orderWrites(x);
+ * }
+ *
+ * // ...
+ * }}</pre>
+ *
+ * Because this construction avoids use of {@code orderAccesses},
+ * which is typically more costly than the other fence methods, it may
+ * result in better performance than using {@code volatile} or its
+ * emulation. However, as is the case with most applications of fence
+ * methods, correctness relies on the usage context -- here, the
+ * thread safety of {@code Item}, as well as the lack of need for full
+ * volatile semantics inside this class itself. However, the second
+ * concern means that it can be difficult to extend the {@code
+ * ItemHolder} class in this example to be more useful.
+ *
+ * <p><b>Avoiding premature finalization.</b> Finalization may occur
+ * whenever a Java Virtual Machine detects that no reference to an
+ * object will ever be stored in the heap: A garbage collector may
+ * reclaim an object even if the fields of that object are still in
+ * use, so long as the object has otherwise become unreachable. This
+ * may have surprising and undesirable effects in cases such as the
+ * following example in which the bookkeeping associated with a class
+ * is managed through array indices. Here, method {@code action}
+ * uses a {@code reachabilityFence} to ensure that the Resource
+ * object is not reclaimed before bookkeeping on an associated
+ * ExternalResource has been performed; in particular here, to ensure
+ * that the array slot holding the ExternalResource is not nulled out
+ * in method {@link Object#finalize}, which may otherwise run
+ * concurrently.
+ *
+ * <pre> {@code
+ * class Resource {
+ * private static ExternalResource[] externalResourceArray = ...
+ *
+ * int myIndex;
+ * Resource(...) {
+ * myIndex = ...
+ * externalResourceArray[myIndex] = ...;
+ * ...
+ * }
+ * protected void finalize() {
+ * externalResourceArray[myIndex] = null;
+ * ...
+ * }
+ * public void action() {
+ * try {
+ * // ...
+ * int i = myIndex;
+ * Resource.update(externalResourceArray[i]);
+ * } finally {
+ * Fences.reachabilityFence(this);
+ * }
+ * }
+ * private static void update(ExternalResource ext) {
+ * ext.status = ...;
+ * }
+ * }}</pre>
+ *
+ * Here, the call to {@code reachabilityFence} is nonintuitively
+ * placed <em>after</em> the call to {@code update}, to ensure that
+ * the array slot is not nulled out by {@link Object#finalize} before
+ * the update, even if the call to {@code action} was the last use of
+ * this object. This might be the case if for example a usage in a
+ * user program had the form {@code new Resource().action();} which
+ * retains no other reference to this Resource. While probably
+ * overkill here, {@code reachabilityFence} is placed in a {@code
+ * finally} block to ensure that it is invoked across all paths in the
+ * method. In a method with more complex control paths, you might
+ * need further precautions to ensure that {@code reachabilityFence}
+ * is encountered along all of them.
+ *
+ * <p>It is sometimes possible to better encapsulate use of
+ * {@code reachabilityFence}. Continuing the above example, if it
+ * were OK for the call to method update to proceed even if the
+ * finalizer had already executed (nulling out slot), then you could
+ * localize use of {@code reachabilityFence}:
+ *
+ * <pre> {@code
+ * public void action2() {
+ * // ...
+ * Resource.update(getExternalResource());
+ * }
+ * private ExternalResource getExternalResource() {
+ * ExternalResource ext = externalResourceArray[myIndex];
+ * Fences.reachabilityFence(this);
+ * return ext;
+ * }}</pre>
+ *
+ * <p>Method {@code reachabilityFence} is not required in
+ * constructions that themselves ensure reachability. For example,
+ * because objects that are locked cannot in general be reclaimed, it
+ * would suffice if all accesses of the object, in all methods of
+ * class Resource (including {@code finalize}) were enclosed in {@code
+ * synchronized (this)} blocks. (Further, such blocks must not include
+ * infinite loops, or themselves be unreachable, which fall into the
+ * corner case exceptions to the "in general" disclaimer.) However,
+ * method {@code reachabilityFence} remains a better option in cases
+ * where this approach is not as efficient, desirable, or possible;
+ * for example because it would encounter deadlock.
+ *
+ * <p><b>Formal Properties.</b>
+ *
+ * <p>Using the terminology of The Java Language Specification chapter
+ * 17, the rules governing the semantics of the methods of this class
+ * are as follows:
+ *
+ * <p> The following is still under construction.
+ *
+ * <dl>
+ *
+ * <dt><b>[Definitions]</b>
+ * <dd>
+ * <ul>
+ *
+ * <li>Define <em>sequenced(a, b)</em> to be true if <em>a</em>
+ * occurs before <em>b</em> in <em>program order</em>.
+ *
+ * <li>Define <em>accesses(a, p)</em> to be true if
+ * <em>a</em> is a read or write of a field (or if an array, an
+ * element) of the object referenced by <em>p</em>.
+ *
+ * <li>Define <em>deeplyAccesses(a, p)</em> to be true if either
+ * <em>accesses(a, p)</em> or <em>deeplyAccesses(a, q)</em> where
+ * <em>q</em> is the value seen by some read <em>r</em>
+ * such that <em>accesses(r, p)</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Matching]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li><em>w</em>, a write of value <em>p</em>
+ *
+ * <li> <em>rf</em>, an invocation of {@code orderReads(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li> <em>r</em>, a read returning value <em>p</em>
+ *
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(wf, w)
+ * <li>read <em>r</em> sees write <em>w</em>
+ * <li>sequenced(r, rf)
+ * </ul>
+ * Then:
+ * <ul>
+ *
+ * <li> <em>wf happens-before rf</em>
+ *
+ * <li> <em>wf</em> precedes <em>rf</em> in the
+ * <em>synchronization order</em>
+ *
+ * <li> If (<em>r1</em>, <em>w1</em>) and (<em>r2</em>,
+ * <em>w2</em>) are two pairs of reads and writes, both
+ * respectively satisfying the above conditions for <em>p</em>,
+ * and sequenced(r1, r2) then it is not the case that <em>w2
+ * happens-before w1</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Initial Reads]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li> <em>a</em>, an access where deeplyAccesses(a, p)
+ *
+ * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li><em>w</em>, a write of value <em>p</em>
+ *
+ * <li> <em>r</em>, a read returning value <em>p</em>
+ *
+ * <li> <em>b</em>, an access where accesses(b, p)
+ *
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(a, wf);
+ * <li>sequenced(wf, w)
+ * <li>read <em>r</em> sees write <em>w</em>, and
+ * <em>r</em> is the first read by some thread
+ * <em>t</em> that sees value <em>p</em>
+ * <li>sequenced(r, b)
+ * </ul>
+ * Then:
+ * <ul>
+ * <li> the effects of <em>b</em> are constrained
+ * by the relation <em>a happens-before b</em>.
+ * </ul>
+ * <p>
+ * <dt><b>[orderAccesses]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ * <li><em>p</em>, a reference to an object
+ * <li><em>f</em>, an invocation of {@code orderAccesses(p)}
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(f, w)
+ * </ul>
+ *
+ * Then:
+ *
+ * <ul>
+ *
+ * <li> <em>f</em> is an element of the <em>synchronization order</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Reachability]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li><em>f</em>, an invocation of {@code reachabilityFence(p)}
+ *
+ * <li><em>a</em>, an access where accesses(a, p)
+ *
+ * <li><em>b</em>, an action (by a garbage collector) taking
+ * the form of an invocation of {@code
+ * p.finalize()} or of enqueing any {@link
+ * java.lang.ref.Reference} constructed with argument <em>p</em>
+ *
+ * </ul>
+ *
+ * If:
+ * <ul>
+ * <li>sequenced(a, f)
+ * </ul>
+ *
+ * Then:
+ *
+ * <ul>
+ *
+ * <li> <em>a happens-before b</em>.
+ *
+ * </ul>
+ *
+ * </dl>
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class Fences {
+ private Fences() {} // Non-instantiable
+
+ /*
+ * The methods of this class are intended to be intrinisified by a
+ * JVM. However, we provide correct but inefficient Java-level
+ * code that simply reads and writes a static volatile
+ * variable. Without JVM support, the consistency effects are
+ * stronger than necessary, and the memory contention effects can
+ * be a serious performance issue.
+ */
+ private static volatile int theVolatile;
+
+ /**
+ * Informally: Ensures that a read of the given reference prior to
+ * the invocation of this method occurs before a subsequent use of
+ * the given reference with the effect of reading or writing a
+ * field (or if an array, element) of the referenced object. The
+ * use of this method is sensible only when paired with other
+ * invocations of {@link #orderWrites} and/or {@link
+ * #orderAccesses} for the given reference. For details, see the
+ * class documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderReads(T ref) {
+ int ignore = theVolatile;
+ return ref;
+ }
+
+ /**
+ * Informally: Ensures that a use of the given reference with the
+ * effect of reading or writing a field (or if an array, element)
+ * of the referenced object, prior to the invocation of this
+ * method occur before a subsequent write of the reference. For
+ * details, see the class documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderWrites(T ref) {
+ theVolatile = 0;
+ return ref;
+ }
+
+ /**
+ * Informally: Ensures that accesses (reads or writes) using the
+ * given reference prior to the invocation of this method occur
+ * before subsequent accesses. For details, see the class
+ * documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderAccesses(T ref) {
+ theVolatile = 0;
+ return ref;
+ }
+
+ /**
+ * Ensures that the object referenced by the given reference
+ * remains <em>strongly reachable</em> (as defined in the {@link
+ * java.lang.ref} package documentation), regardless of any prior
+ * actions of the program that might otherwise cause the object to
+ * become unreachable; thus, the referenced object is not
+ * reclaimable by garbage collection at least until after the
+ * invocation of this method. Invocation of this method does not
+ * itself initiate garbage collection or finalization.
+ *
+ * <p>See the class-level documentation for further explanation
+ * and usage examples.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ */
+ public static void reachabilityFence(Object ref) {
+ if (ref != null) {
+ synchronized (ref) {}
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/package-info.java b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
index 4a4375d..efbb413 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
@@ -11,9 +11,7 @@
* array elements to those that also provide an atomic conditional update
* operation of the form:
*
- * <pre>
- * boolean compareAndSet(expectedValue, updateValue);
- * </pre>
+ * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
*
* <p>This method (which varies in argument types across different
* classes) atomically sets a variable to the {@code updateValue} if it
@@ -40,15 +38,30 @@
* {@code AtomicInteger} provide atomic increment methods. One
* application is to generate sequence numbers, as in:
*
- * <pre>
+ * <pre> {@code
* class Sequencer {
* private final AtomicLong sequenceNumber
* = new AtomicLong(0);
* public long next() {
* return sequenceNumber.getAndIncrement();
* }
- * }
- * </pre>
+ * }}</pre>
+ *
+ * <p>It is straightforward to define new utility functions that, like
+ * {@code getAndIncrement}, apply a function to a value atomically.
+ * For example, given some transformation
+ * <pre> {@code long transform(long input)}</pre>
+ *
+ * write your utility method as follows:
+ * <pre> {@code
+ * boolean getAndTransform(AtomicLong var) {
+ * while (true) {
+ * long current = var.get();
+ * long next = transform(current);
+ * if (var.compareAndSet(current, next))
+ * return current;
+ * }
+ * }}</pre>
*
* <p>The memory effects for accesses and updates of atomics generally
* follow the rules for volatiles, as stated in
@@ -161,9 +174,9 @@
* {@code byte} values, and cast appropriately.
*
* You can also hold floats using
- * {@link java.lang.Float#floatToIntBits} and
+ * {@link java.lang.Float#floatToRawIntBits} and
* {@link java.lang.Float#intBitsToFloat} conversions, and doubles using
- * {@link java.lang.Double#doubleToLongBits} and
+ * {@link java.lang.Double#doubleToRawLongBits} and
* {@link java.lang.Double#longBitsToDouble} conversions.
*
* @since 1.5
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
index f3780e5..4bec0cf 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
index 5c8111c..7b36460 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import sun.misc.Unsafe;
/**
@@ -569,7 +568,7 @@ public abstract class AbstractQueuedLongSynchronizer
/**
* Convenience method to interrupt current thread.
*/
- private static void selfInterrupt() {
+ static void selfInterrupt() {
Thread.currentThread().interrupt();
}
@@ -1231,7 +1230,7 @@ public abstract class AbstractQueuedLongSynchronizer
* due to the queue being empty.
*
* <p>This method is designed to be used by a fair synchronizer to
- * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>.
+ * avoid <a href="AbstractQueuedSynchronizer.html#barging">barging</a>.
* Such a synchronizer's {@link #tryAcquire} method should return
* {@code false}, and its {@link #tryAcquireShared} method should
* return a negative value, if this method returns {@code true}
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index 065f130..42029f0 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import sun.misc.Unsafe;
// BEGIN android-note
@@ -173,7 +172,7 @@ import sun.misc.Unsafe;
* It also supports conditions and exposes
* one of the instrumentation methods:
*
- * <pre>
+ * <pre> {@code
* class Mutex implements Lock, java.io.Serializable {
*
* // Our internal helper class
@@ -229,15 +228,14 @@ import sun.misc.Unsafe;
* throws InterruptedException {
* return sync.tryAcquireNanos(1, unit.toNanos(timeout));
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Here is a latch class that is like a {@link CountDownLatch}
* except that it only requires a single <tt>signal</tt> to
* fire. Because a latch is non-exclusive, it uses the <tt>shared</tt>
* acquire and release methods.
*
- * <pre>
+ * <pre> {@code
* class BooleanLatch {
*
* private static class Sync extends AbstractQueuedSynchronizer {
@@ -259,8 +257,7 @@ import sun.misc.Unsafe;
* public void await() throws InterruptedException {
* sync.acquireSharedInterruptibly(1);
* }
- * }
- * </pre>
+ * }}</pre>
*
* @since 1.5
* @author Doug Lea
@@ -800,7 +797,7 @@ public abstract class AbstractQueuedSynchronizer
/**
* Convenience method to interrupt current thread.
*/
- private static void selfInterrupt() {
+ static void selfInterrupt() {
Thread.currentThread().interrupt();
}
@@ -2251,9 +2248,7 @@ public abstract class AbstractQueuedSynchronizer
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- // BEGIN android-changed
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE;
- // END android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
diff --git a/luni/src/main/java/java/util/concurrent/locks/Condition.java b/luni/src/main/java/java/util/concurrent/locks/Condition.java
index 8504e1f..7050df9 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Condition.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Condition.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
@@ -331,10 +331,9 @@ public interface Condition {
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method is behaviorally
- * equivalent to:<br>
- * <pre>
- * awaitNanos(unit.toNanos(time)) &gt; 0
- * </pre>
+ * equivalent to:
+ * <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
+ *
* @param time the maximum time to wait
* @param unit the time unit of the {@code time} argument
* @return {@code false} if the waiting time detectably elapsed
diff --git a/luni/src/main/java/java/util/concurrent/locks/Lock.java b/luni/src/main/java/java/util/concurrent/locks/Lock.java
index 4b9abd6..d5c6294 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Lock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Lock.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
@@ -48,14 +48,14 @@ import java.util.concurrent.TimeUnit;
* methods and statements. In most cases, the following idiom
* should be used:
*
- * <pre><tt> Lock l = ...;
- * l.lock();
- * try {
- * // access the resource protected by this lock
- * } finally {
- * l.unlock();
- * }
- * </tt></pre>
+ * <pre> {@code
+ * Lock l = ...;
+ * l.lock();
+ * try {
+ * // access the resource protected by this lock
+ * } finally {
+ * l.unlock();
+ * }}</pre>
*
* When locking and unlocking occur in different scopes, care must be
* taken to ensure that all code that is executed while the lock is
@@ -210,18 +210,18 @@ public interface Lock {
* immediately with the value {@code false}.
*
* <p>A typical usage idiom for this method would be:
- * <pre>
- * Lock lock = ...;
- * if (lock.tryLock()) {
- * try {
- * // manipulate protected state
- * } finally {
- * lock.unlock();
- * }
- * } else {
- * // perform alternative actions
- * }
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock()) {
+ * try {
+ * // manipulate protected state
+ * } finally {
+ * lock.unlock();
+ * }
+ * } else {
+ * // perform alternative actions
+ * }}</pre>
+ *
* This usage ensures that the lock is unlocked if it was acquired, and
* doesn't try to unlock if the lock was not acquired.
*
diff --git a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
index 0c0f07d..422e428 100644
--- a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
+++ b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
@@ -1,11 +1,10 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
-import java.util.concurrent.*;
import sun.misc.Unsafe;
@@ -49,7 +48,10 @@ import sun.misc.Unsafe;
* higher-level synchronization utilities, and are not in themselves
* useful for most concurrency control applications. The {@code park}
* method is designed for use only in constructions of the form:
- * <pre>while (!canProceed()) { ... LockSupport.park(this); }</pre>
+ *
+ * <pre> {@code
+ * while (!canProceed()) { ... LockSupport.park(this); }}</pre>
+ *
* where neither {@code canProceed} nor any other actions prior to the
* call to {@code park} entail locking or blocking. Because only one
* permit is associated with each thread, any intermediary uses of
@@ -57,7 +59,7 @@ import sun.misc.Unsafe;
*
* <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out
* non-reentrant lock class:
- * <pre>{@code
+ * <pre> {@code
* class FIFOMutex {
* private final AtomicBoolean locked = new AtomicBoolean(false);
* private final Queue<Thread> waiters
@@ -92,7 +94,7 @@ public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
// Hotspot implementation via intrinsics API
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
static {
@@ -246,10 +248,14 @@ public class LockSupport {
* snapshot -- the thread may have since unblocked or blocked on a
* different blocker object.
*
+ * @param t the thread
* @return the blocker
+ * @throws NullPointerException if argument is null
* @since 1.6
*/
public static Object getBlocker(Thread t) {
+ if (t == null)
+ throw new NullPointerException();
return unsafe.getObjectVolatile(t, parkBlockerOffset);
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
index 484f68d..bb7b388 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
index cf787ca..07baf41 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
@@ -44,7 +43,7 @@ import java.util.concurrent.atomic.*;
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
- * <pre>
+ * <pre> {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
@@ -57,8 +56,7 @@ import java.util.concurrent.atomic.*;
* lock.unlock()
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>In addition to implementing the {@link Lock} interface, this
* class defines methods {@code isLocked} and
@@ -354,8 +352,11 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* method. If you want a timed {@code tryLock} that does permit barging on
* a fair lock then combine the timed and un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the current thread
* already holds this lock then the hold count is incremented by one and
@@ -485,7 +486,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* not be entered with the lock already held then we can assert that
* fact:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -498,8 +499,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* lock.unlock();
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* @return the number of holds on this lock by the current thread,
* or zero if this lock is not held by the current thread
@@ -516,7 +516,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* testing. For example, a method that should only be called while
* a lock is held can assert that this is the case:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -525,13 +525,12 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* assert lock.isHeldByCurrentThread();
* // ... method body
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>It can also be used to ensure that a reentrant lock is used
* in a non-reentrant manner, for example:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -545,8 +544,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* lock.unlock();
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* @return {@code true} if current thread holds this lock and
* {@code false} otherwise
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
index b1a1a60..244a4a7 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import java.util.*;
/**
@@ -33,7 +32,7 @@ import java.util.*;
* <dt><b><i>Fair mode</i></b>
* <dd> When constructed as fair, threads contend for entry using an
* approximately arrival-order policy. When the currently held lock
- * is released either the longest-waiting single writer thread will
+ * is released, either the longest-waiting single writer thread will
* be assigned the write lock, or if there is a group of reader threads
* waiting longer than all waiting writer threads, that group will be
* assigned the read lock.
@@ -51,8 +50,8 @@ import java.util.*;
* will block unless both the read lock and write lock are free (which
* implies there are no waiting threads). (Note that the non-blocking
* {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods
- * do not honor this fair setting and will acquire the lock if it is
- * possible, regardless of waiting threads.)
+ * do not honor this fair setting and will immediately acquire the lock
+ * if it is possible, regardless of waiting threads.)
* <p>
* </dl>
*
@@ -114,21 +113,21 @@ import java.util.*;
* void processCachedData() {
* rwl.readLock().lock();
* if (!cacheValid) {
- * // Must release read lock before acquiring write lock
- * rwl.readLock().unlock();
- * rwl.writeLock().lock();
- * try {
- * // Recheck state because another thread might have
- * // acquired write lock and changed state before we did.
- * if (!cacheValid) {
- * data = ...
- * cacheValid = true;
- * }
- * // Downgrade by acquiring read lock before releasing write lock
- * rwl.readLock().lock();
- * } finally {
- * rwl.writeLock().unlock(); // Unlock write, still hold read
- * }
+ * // Must release read lock before acquiring write lock
+ * rwl.readLock().unlock();
+ * rwl.writeLock().lock();
+ * try {
+ * // Recheck state because another thread might have
+ * // acquired write lock and changed state before we did.
+ * if (!cacheValid) {
+ * data = ...
+ * cacheValid = true;
+ * }
+ * // Downgrade by acquiring read lock before releasing write lock
+ * rwl.readLock().lock();
+ * } finally {
+ * rwl.writeLock().unlock(); // Unlock write, still hold read
+ * }
* }
*
* try {
@@ -147,33 +146,33 @@ import java.util.*;
* is a class using a TreeMap that is expected to be large and
* concurrently accessed.
*
- * <pre>{@code
+ * <pre> {@code
* class RWDictionary {
- * private final Map<String, Data> m = new TreeMap<String, Data>();
- * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- * private final Lock r = rwl.readLock();
- * private final Lock w = rwl.writeLock();
+ * private final Map<String, Data> m = new TreeMap<String, Data>();
+ * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ * private final Lock r = rwl.readLock();
+ * private final Lock w = rwl.writeLock();
*
- * public Data get(String key) {
- * r.lock();
- * try { return m.get(key); }
- * finally { r.unlock(); }
- * }
- * public String[] allKeys() {
- * r.lock();
- * try { return m.keySet().toArray(); }
- * finally { r.unlock(); }
- * }
- * public Data put(String key, Data value) {
- * w.lock();
- * try { return m.put(key, value); }
- * finally { w.unlock(); }
- * }
- * public void clear() {
- * w.lock();
- * try { m.clear(); }
- * finally { w.unlock(); }
- * }
+ * public Data get(String key) {
+ * r.lock();
+ * try { return m.get(key); }
+ * finally { r.unlock(); }
+ * }
+ * public String[] allKeys() {
+ * r.lock();
+ * try { return m.keySet().toArray(); }
+ * finally { r.unlock(); }
+ * }
+ * public Data put(String key, Data value) {
+ * w.lock();
+ * try { return m.put(key, value); }
+ * finally { w.unlock(); }
+ * }
+ * public void clear() {
+ * w.lock();
+ * try { m.clear(); }
+ * finally { w.unlock(); }
+ * }
* }}</pre>
*
* <h3>Implementation Notes</h3>
@@ -625,7 +624,9 @@ public class ReentrantReadWriteLock
}
/**
- * Reconstitute this lock instance from a stream
+ * Reconstitutes this lock instance from a stream (that is,
+ * deserializes it).
+ *
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
@@ -790,8 +791,11 @@ public class ReentrantReadWriteLock
* permit barging on a fair lock then combine the timed and
* un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the write lock is held by another thread then the
* current thread becomes disabled for thread scheduling
@@ -1020,8 +1024,11 @@ public class ReentrantReadWriteLock
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
diff --git a/luni/src/main/java/java/util/concurrent/locks/package-info.java b/luni/src/main/java/java/util/concurrent/locks/package-info.java
index 860acdd..433f869 100644
--- a/luni/src/main/java/java/util/concurrent/locks/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/locks/package-info.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
diff --git a/luni/src/main/java/java/util/concurrent/package-info.java b/luni/src/main/java/java/util/concurrent/package-info.java
index 8509a41..155d1b8 100644
--- a/luni/src/main/java/java/util/concurrent/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/package-info.java
@@ -1,11 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
// BEGIN android-note
-// Dropped references to unreleased APIs (ForkJoinPool, Phaser, etc.)
+// omit links to ForkJoinPool, ForkJoinTask, LinkedTransferQueue, PHaser, TransferQueue
// END android-note
/**
@@ -111,7 +111,7 @@
*
* <h2>Synchronizers</h2>
*
- * Five classes aid common special-purpose synchronization idioms.
+ * Four classes aid common special-purpose synchronization idioms.
* <ul>
*
* <li>{@link java.util.concurrent.Semaphore} is a classic concurrency tool.
@@ -146,7 +146,7 @@
* A {@code CopyOnWriteArrayList} is preferable to a synchronized
* {@code ArrayList} when the expected number of reads and traversals
* greatly outnumber the number of updates to a list.
-
+ *
* <p>The "Concurrent" prefix used with some classes in this package
* is a shorthand indicating several differences from similar
* "synchronized" classes. For example {@code java.util.Hashtable} and
@@ -243,7 +243,8 @@
* in each thread <i>happen-before</i> those subsequent to the
* corresponding {@code exchange()} in another thread.
*
- * <li>Actions prior to calling {@code CyclicBarrier.await}
+ * <li>Actions prior to calling {@code CyclicBarrier.await} and
+ * {@code Phaser.awaitAdvance} (as well as its variants)
* <i>happen-before</i> actions performed by the barrier action, and
* actions performed by the barrier action <i>happen-before</i> actions
* subsequent to a successful return from the corresponding {@code await}
diff --git a/luni/src/main/java/java/util/jar/JarOutputStream.java b/luni/src/main/java/java/util/jar/JarOutputStream.java
index 78e3aa5..fc0645e 100644
--- a/luni/src/main/java/java/util/jar/JarOutputStream.java
+++ b/luni/src/main/java/java/util/jar/JarOutputStream.java
@@ -37,20 +37,20 @@ public class JarOutputStream extends ZipOutputStream {
*
* @param os
* the {@code OutputStream} to write to
- * @param mf
+ * @param manifest
* the {@code Manifest} to output for this JAR file.
* @throws IOException
* if an error occurs creating the {@code JarOutputStream}.
*/
- public JarOutputStream(OutputStream os, Manifest mf) throws IOException {
+ public JarOutputStream(OutputStream os, Manifest manifest) throws IOException {
super(os);
- if (mf == null) {
- throw new NullPointerException();
+ if (manifest == null) {
+ throw new NullPointerException("manifest == null");
}
- manifest = mf;
+ this.manifest = manifest;
ZipEntry ze = new ZipEntry(JarFile.MANIFEST_NAME);
putNextEntry(ze);
- manifest.write(this);
+ this.manifest.write(this);
closeEntry();
}
diff --git a/luni/src/main/java/java/util/logging/FileHandler.java b/luni/src/main/java/java/util/logging/FileHandler.java
index 980955a..6ffef87 100644
--- a/luni/src/main/java/java/util/logging/FileHandler.java
+++ b/luni/src/main/java/java/util/logging/FileHandler.java
@@ -214,8 +214,10 @@ public class FileHandler extends StreamHandler {
String className = this.getClass().getName();
pattern = (p == null) ? getStringProperty(className + ".pattern",
DEFAULT_PATTERN) : p;
- if (pattern == null || pattern.isEmpty()) {
- throw new NullPointerException("Pattern cannot be empty or null");
+ if (pattern == null) {
+ throw new NullPointerException("pattern == null");
+ } else if (pattern.isEmpty()) {
+ throw new NullPointerException("pattern.isEmpty()");
}
append = (a == null) ? getBooleanProperty(className + ".append",
DEFAULT_APPEND) : a.booleanValue();
diff --git a/luni/src/main/java/java/util/logging/Handler.java b/luni/src/main/java/java/util/logging/Handler.java
index 5a6c14e..13dbdd5 100644
--- a/luni/src/main/java/java/util/logging/Handler.java
+++ b/luni/src/main/java/java/util/logging/Handler.java
@@ -227,7 +227,7 @@ public abstract class Handler {
*/
public boolean isLoggable(LogRecord record) {
if (record == null) {
- throw new NullPointerException();
+ throw new NullPointerException("record == null");
}
if (this.level.intValue() == Level.OFF.intValue()) {
return false;
@@ -294,17 +294,17 @@ public abstract class Handler {
/**
* Sets the error manager for this handler.
*
- * @param em
+ * @param newErrorManager
* the error manager to set.
* @throws NullPointerException
* if {@code em} is {@code null}.
*/
- public void setErrorManager(ErrorManager em) {
+ public void setErrorManager(ErrorManager newErrorManager) {
LogManager.getLogManager().checkAccess();
- if (em == null) {
- throw new NullPointerException();
+ if (newErrorManager == null) {
+ throw new NullPointerException("newErrorManager == null");
}
- this.errorMan = em;
+ this.errorMan = newErrorManager;
}
/**
@@ -327,7 +327,7 @@ public abstract class Handler {
*/
void internalSetFormatter(Formatter newFormatter) {
if (newFormatter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("newFormatter == null");
}
this.formatter = newFormatter;
}
@@ -356,7 +356,7 @@ public abstract class Handler {
*/
public void setLevel(Level newLevel) {
if (newLevel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("newLevel == null");
}
LogManager.getLogManager().checkAccess();
this.level = newLevel;
diff --git a/luni/src/main/java/java/util/logging/LogManager.java b/luni/src/main/java/java/util/logging/LogManager.java
index 449c263..8877cd5 100644
--- a/luni/src/main/java/java/util/logging/LogManager.java
+++ b/luni/src/main/java/java/util/logging/LogManager.java
@@ -446,7 +446,7 @@ public class LogManager {
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
if (l == null) {
- throw new NullPointerException();
+ throw new NullPointerException("l == null");
}
checkAccess();
listeners.addPropertyChangeListener(l);
diff --git a/luni/src/main/java/java/util/logging/StreamHandler.java b/luni/src/main/java/java/util/logging/StreamHandler.java
index 7581835..60b4321 100644
--- a/luni/src/main/java/java/util/logging/StreamHandler.java
+++ b/luni/src/main/java/java/util/logging/StreamHandler.java
@@ -167,7 +167,7 @@ public class StreamHandler extends Handler {
*/
protected void setOutputStream(OutputStream os) {
if (os == null) {
- throw new NullPointerException();
+ throw new NullPointerException("os == null");
}
LogManager.getLogManager().checkAccess();
close(true);
diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
index d86d789..71110c3 100644
--- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java
+++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -372,7 +372,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public void exportNode(OutputStream ostream) throws IOException, BackingStoreException {
if (ostream == null) {
- throw new NullPointerException("Stream is null");
+ throw new NullPointerException("ostream == null");
}
checkState();
XMLParser.exportPrefs(this, ostream, false);
@@ -381,7 +381,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public void exportSubtree(OutputStream ostream) throws IOException, BackingStoreException {
if (ostream == null) {
- throw new NullPointerException("Stream is null");
+ throw new NullPointerException("ostream == null");
}
checkState();
XMLParser.exportPrefs(this, ostream, true);
@@ -402,7 +402,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public String get(String key, String deflt) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
String result = null;
synchronized (lock) {
@@ -597,7 +597,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public boolean nodeExists(String name) throws BackingStoreException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
AbstractPreferences startNode = null;
synchronized (lock) {
@@ -640,8 +640,10 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public void put(String key, String value) {
- if (key == null || value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
if (key.length() > MAX_KEY_LENGTH || value.length() > MAX_VALUE_LENGTH) {
throw new IllegalArgumentException();
@@ -730,7 +732,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public void addNodeChangeListener(NodeChangeListener ncl) {
if (ncl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("ncl == null");
}
checkState();
synchronized (nodeChangeListeners) {
@@ -741,7 +743,7 @@ public abstract class AbstractPreferences extends Preferences {
@Override
public void addPreferenceChangeListener(PreferenceChangeListener pcl) {
if (pcl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pcl == null");
}
checkState();
synchronized (preferenceChangeListeners) {
diff --git a/luni/src/main/java/java/util/regex/Matcher.java b/luni/src/main/java/java/util/regex/Matcher.java
index 58bcf9e..cfd4432 100644
--- a/luni/src/main/java/java/util/regex/Matcher.java
+++ b/luni/src/main/java/java/util/regex/Matcher.java
@@ -50,11 +50,6 @@ public final class Matcher implements MatchResult {
private int regionEnd;
/**
- * Holds the position where the next find operation will take place.
- */
- private int findPos;
-
- /**
* Holds the position where the next append operation will take place.
*/
private int appendPos;
@@ -212,7 +207,6 @@ public final class Matcher implements MatchResult {
resetForInput();
matchFound = false;
- findPos = regionStart;
appendPos = 0;
return this;
@@ -377,30 +371,17 @@ public final class Matcher implements MatchResult {
}
/**
- * Returns the next occurrence of the {@link Pattern} in the input. The
- * method starts the search from the given character in the input.
+ * Returns true if there is another match in the input, starting
+ * from the given position. The region is ignored.
*
- * @param start
- * The index in the input at which the find operation is to
- * begin. If this is less than the start of the region, it is
- * automatically adjusted to that value. If it is beyond the end
- * of the region, the method will fail.
- * @return true if (and only if) a match has been found.
+ * @throws IndexOutOfBoundsException if {@code start < 0 || start > input.length()}
*/
public boolean find(int start) {
- findPos = start;
-
- if (findPos < regionStart) {
- findPos = regionStart;
- } else if (findPos >= regionEnd) {
- matchFound = false;
- return false;
+ if (start < 0 || start > input.length()) {
+ throw new IndexOutOfBoundsException("start=" + start + "; length=" + input.length());
}
- matchFound = findImpl(address, input, findPos, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
+ matchFound = findImpl(address, input, start, matchOffsets);
return matchFound;
}
@@ -414,9 +395,6 @@ public final class Matcher implements MatchResult {
*/
public boolean find() {
matchFound = findNextImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
@@ -429,9 +407,6 @@ public final class Matcher implements MatchResult {
*/
public boolean lookingAt() {
matchFound = lookingAtImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
@@ -444,9 +419,6 @@ public final class Matcher implements MatchResult {
*/
public boolean matches() {
matchFound = matchesImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
diff --git a/luni/src/main/java/java/util/regex/Pattern.java b/luni/src/main/java/java/util/regex/Pattern.java
index 46984b9..cbd5965 100644
--- a/luni/src/main/java/java/util/regex/Pattern.java
+++ b/luni/src/main/java/java/util/regex/Pattern.java
@@ -82,16 +82,23 @@ import java.io.Serializable;
* </table>
* <p>Most of the time, the built-in character classes are more useful:
* <table>
- * <tr> <td> \d </td> <td>Any digit character.</td> </tr>
- * <tr> <td> \D </td> <td>Any non-digit character.</td> </tr>
- * <tr> <td> \s </td> <td>Any whitespace character.</td> </tr>
- * <tr> <td> \S </td> <td>Any non-whitespace character.</td> </tr>
- * <tr> <td> \w </td> <td>Any word character.</td> </tr>
- * <tr> <td> \W </td> <td>Any non-word character.</td> </tr>
+ * <tr> <td> \d </td> <td>Any digit character (see note below).</td> </tr>
+ * <tr> <td> \D </td> <td>Any non-digit character (see note below).</td> </tr>
+ * <tr> <td> \s </td> <td>Any whitespace character (see note below).</td> </tr>
+ * <tr> <td> \S </td> <td>Any non-whitespace character (see note below).</td> </tr>
+ * <tr> <td> \w </td> <td>Any word character (see note below).</td> </tr>
+ * <tr> <td> \W </td> <td>Any non-word character (see note below).</td> </tr>
* <tr> <td> \p{<i>NAME</i>} </td> <td> Any character in the class with the given <i>NAME</i>. </td> </tr>
* <tr> <td> \P{<i>NAME</i>} </td> <td> Any character <i>not</i> in the named class. </td> </tr>
* </table>
- * <p>There are a variety of named classes:
+ * <p>Note that these built-in classes don't just cover the traditional ASCII range. For example,
+ * <code>\w</code> is equivalent to the character class <code>[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}]</code>.
+ * For more details see <a href="http://www.unicode.org/reports/tr18/#Compatibility_Properties">Unicode TR-18</a>,
+ * and bear in mind that the set of characters in each class can vary between Unicode releases.
+ * If you actually want to match only ASCII characters, specify the explicit characters you want;
+ * if you mean 0-9 use <code>[0-9]</code> rather than <code>\d</code>, which would also include
+ * Gurmukhi digits and so forth.
+ * <p>There are also a variety of named classes:
* <ul>
* <li><a href="../../lang/Character.html#unicode_categories">Unicode category names</a>,
* prefixed by {@code Is}. For example {@code \p{IsLu}} for all uppercase letters.
diff --git a/luni/src/main/java/java/util/zip/DeflaterInputStream.java b/luni/src/main/java/java/util/zip/DeflaterInputStream.java
index c6e95f2..805ce17 100644
--- a/luni/src/main/java/java/util/zip/DeflaterInputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterInputStream.java
@@ -72,8 +72,10 @@ public class DeflaterInputStream extends FilterInputStream {
*/
public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) {
super(in);
- if (in == null || deflater == null) {
- throw new NullPointerException();
+ if (in == null) {
+ throw new NullPointerException("in == null");
+ } else if (deflater == null) {
+ throw new NullPointerException("deflater == null");
}
if (bufferSize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
index b0bcb99..d336e72 100644
--- a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -115,8 +115,10 @@ public class DeflaterOutputStream extends FilterOutputStream {
*/
public DeflaterOutputStream(OutputStream os, Deflater def, int bsize, boolean syncFlush) {
super(os);
- if (os == null || def == null) {
- throw new NullPointerException();
+ if (os == null) {
+ throw new NullPointerException("os == null");
+ } else if (def == null) {
+ throw new NullPointerException("def == null");
}
if (bsize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/InflaterInputStream.java b/luni/src/main/java/java/util/zip/InflaterInputStream.java
index 580d605..6081037 100644
--- a/luni/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/luni/src/main/java/java/util/zip/InflaterInputStream.java
@@ -103,8 +103,10 @@ public class InflaterInputStream extends FilterInputStream {
*/
public InflaterInputStream(InputStream is, Inflater inflater, int bsize) {
super(is);
- if (is == null || inflater == null) {
- throw new NullPointerException();
+ if (is == null) {
+ throw new NullPointerException("is == null");
+ } else if (inflater == null) {
+ throw new NullPointerException("inflater == null");
}
if (bsize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/InflaterOutputStream.java b/luni/src/main/java/java/util/zip/InflaterOutputStream.java
index c9687b6..9a699a8 100644
--- a/luni/src/main/java/java/util/zip/InflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/InflaterOutputStream.java
@@ -70,8 +70,10 @@ public class InflaterOutputStream extends FilterOutputStream {
*/
public InflaterOutputStream(OutputStream out, Inflater inf, int bufferSize) {
super(out);
- if (out == null || inf == null) {
- throw new NullPointerException();
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ } else if (inf == null) {
+ throw new NullPointerException("inf == null");
}
if (bufferSize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index 988bd2c..e2bfc8d 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -71,7 +71,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
*/
public ZipEntry(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (name.length() > 0xFFFF) {
throw new IllegalArgumentException("Name too long: " + name.length());
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index 363f57e..6ecd489 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -223,7 +223,7 @@ public class ZipFile implements ZipConstants {
public ZipEntry getEntry(String entryName) {
checkNotClosed();
if (entryName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("entryName == null");
}
ZipEntry ze = mEntries.get(entryName);
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index d082fc7..e7c4566 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -101,7 +101,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants
public ZipInputStream(InputStream stream) {
super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true));
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
}
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 1dacd46..aeb5def 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -141,10 +141,10 @@ public class Cipher {
protected Cipher(CipherSpi cipherSpi, Provider provider,
String transformation) {
if (cipherSpi == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cipherSpi == null");
}
if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
- throw new NullPointerException();
+ throw new NullPointerException("provider == null");
}
this.provider = provider;
this.transformation = transformation;
@@ -1332,7 +1332,7 @@ public class Cipher {
public static final int getMaxAllowedKeyLength(String transformation)
throws NoSuchAlgorithmException {
if (transformation == null) {
- throw new NullPointerException();
+ throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
@@ -1356,7 +1356,7 @@ public class Cipher {
public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
String transformation) throws NoSuchAlgorithmException {
if (transformation == null) {
- throw new NullPointerException();
+ throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
diff --git a/luni/src/main/java/javax/crypto/CipherInputStream.java b/luni/src/main/java/javax/crypto/CipherInputStream.java
index 39dcfda..a59a425 100644
--- a/luni/src/main/java/javax/crypto/CipherInputStream.java
+++ b/luni/src/main/java/javax/crypto/CipherInputStream.java
@@ -135,7 +135,7 @@ public class CipherInputStream extends FilterInputStream {
@Override
public int read(byte[] buf, int off, int len) throws IOException {
if (in == null) {
- throw new NullPointerException("Underlying input stream is null");
+ throw new NullPointerException("in == null");
}
int i;
diff --git a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
index 034f07a..0fb5b76 100644
--- a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
+++ b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
@@ -121,7 +121,7 @@ public class EncryptedPrivateKeyInfo {
* Creates an {@code EncryptedPrivateKeyInfo} instance from an algorithm
* name and its encrypted data.
*
- * @param encrAlgName
+ * @param encryptionAlgorithmName
* the name of an algorithm.
* @param encryptedData
* the encrypted data.
@@ -133,12 +133,12 @@ public class EncryptedPrivateKeyInfo {
* @throws IllegalArgumentException
* if {@code encryptedData} is empty.
*/
- public EncryptedPrivateKeyInfo(String encrAlgName, byte[] encryptedData)
+ public EncryptedPrivateKeyInfo(String encryptionAlgorithmName, byte[] encryptedData)
throws NoSuchAlgorithmException {
- if (encrAlgName == null) {
- throw new NullPointerException("the algName parameter is null");
+ if (encryptionAlgorithmName == null) {
+ throw new NullPointerException("encryptionAlgorithmName == null");
}
- this.algName = encrAlgName;
+ this.algName = encryptionAlgorithmName;
if (!mapAlgName()) {
throw new NoSuchAlgorithmException("Unsupported algorithm: " + this.algName);
}
diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
index 2ac4994..8745b78 100644
--- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -98,7 +98,7 @@ public class ExemptionMechanism {
public static final ExemptionMechanism getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new ExemptionMechanism((ExemptionMechanismSpi) sap.spi, sap.provider, algorithm);
@@ -134,7 +134,7 @@ public class ExemptionMechanism {
throw new NoSuchProviderException(provider);
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
return getInstance(algorithm, impProvider);
}
@@ -159,7 +159,7 @@ public class ExemptionMechanism {
public static final ExemptionMechanism getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null) {
throw new IllegalArgumentException("provider == null");
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index 9c5d86c..51b4cd1 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -99,7 +99,7 @@ public class KeyAgreement {
public static final KeyAgreement getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyAgreement((KeyAgreementSpi) sap.spi, sap.provider, algorithm);
@@ -161,7 +161,7 @@ public class KeyAgreement {
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/KeyGenerator.java b/luni/src/main/java/javax/crypto/KeyGenerator.java
index 77b1a82..606998a 100644
--- a/luni/src/main/java/javax/crypto/KeyGenerator.java
+++ b/luni/src/main/java/javax/crypto/KeyGenerator.java
@@ -98,7 +98,7 @@ public class KeyGenerator {
public static final KeyGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyGenerator((KeyGeneratorSpi) sap.spi, sap.provider, algorithm);
@@ -158,7 +158,7 @@ public class KeyGenerator {
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyGenerator((KeyGeneratorSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index 1a05b59..46be141 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -101,7 +101,7 @@ public class Mac implements Cloneable {
public static final Mac getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new Mac((MacSpi) sap.spi, sap.provider, algorithm);
@@ -163,7 +163,7 @@ public class Mac implements Cloneable {
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new Mac((MacSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/SealedObject.java b/luni/src/main/java/javax/crypto/SealedObject.java
index c9c1534..cfb970b 100644
--- a/luni/src/main/java/javax/crypto/SealedObject.java
+++ b/luni/src/main/java/javax/crypto/SealedObject.java
@@ -33,12 +33,12 @@ import java.security.NoSuchProviderException;
/**
* A {@code SealedObject} is a wrapper around a {@code serializable} object
* instance and encrypts it using a cryptographic cipher.
- * <p>
- * Since a {@code SealedObject} instance is a serializable object itself it can
+ *
+ * <p>Since a {@code SealedObject} instance is serializable it can
* either be stored or transmitted over an insecure channel.
- * <p>
- * The wrapped object can later be decrypted (unsealed) using the corresponding
- * key and then be deserialized to retrieve the original object.The sealed
+ *
+ * <p>The wrapped object can later be decrypted (unsealed) using the corresponding
+ * key and then be deserialized to retrieve the original object. The sealed
* object itself keeps track of the cipher and corresponding parameters.
*/
public class SealedObject implements Serializable {
@@ -46,19 +46,25 @@ public class SealedObject implements Serializable {
private static final long serialVersionUID = 4482838265551344752L;
/**
- * The {@link AlgorithmParameters} in encoded format.
+ * The cipher's {@link AlgorithmParameters} in encoded format.
+ * Equivalent to {@code cipher.getParameters().getEncoded()},
+ * or null if the cipher did not use any parameters.
*/
protected byte[] encodedParams;
+
private byte[] encryptedContent;
private String sealAlg;
private String paramsAlg;
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
- encodedParams = (byte []) s.readUnshared();
- encryptedContent = (byte []) s.readUnshared();
- sealAlg = (String) s.readUnshared();
- paramsAlg = (String) s.readUnshared();
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
+ // We do unshared reads here to ensure we have our own clones of the byte[]s.
+ encodedParams = (byte[]) s.readUnshared();
+ encryptedContent = (byte[]) s.readUnshared();
+ // These are regular shared reads because the algorithms used by a given stream are
+ // almost certain to the be same for each object, and String is immutable anyway,
+ // so there's no security concern about sharing.
+ sealAlg = (String) s.readObject();
+ paramsAlg = (String) s.readObject();
}
/**
diff --git a/luni/src/main/java/javax/crypto/SecretKeyFactory.java b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
index 5e47abe..8ab3eb8 100644
--- a/luni/src/main/java/javax/crypto/SecretKeyFactory.java
+++ b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
@@ -103,7 +103,7 @@ public class SecretKeyFactory {
public static final SecretKeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new SecretKeyFactory((SecretKeyFactorySpi) sap.spi, sap.provider, algorithm);
@@ -165,7 +165,7 @@ public class SecretKeyFactory {
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new SecretKeyFactory((SecretKeyFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
index fe86aeb..fcfe749 100644
--- a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
+++ b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
@@ -45,7 +45,7 @@ public class DESedeKeySpec implements KeySpec {
*/
public DESedeKeySpec(byte[] key) throws InvalidKeyException {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.length < DES_EDE_KEY_LEN) {
throw new InvalidKeyException();
@@ -71,7 +71,7 @@ public class DESedeKeySpec implements KeySpec {
*/
public DESedeKeySpec(byte[] key, int offset) throws InvalidKeyException {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.length - offset < DES_EDE_KEY_LEN) {
throw new InvalidKeyException();
diff --git a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
index 3bc9ab4..340e57f 100644
--- a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
+++ b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
@@ -73,8 +73,12 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec {
*/
public OAEPParameterSpec(String mdName, String mgfName,
AlgorithmParameterSpec mgfSpec, PSource pSrc) {
- if ((mdName == null) || (mgfName == null) || (pSrc == null)) {
- throw new NullPointerException();
+ if (mdName == null) {
+ throw new NullPointerException("mdName == null");
+ } else if (mgfName == null) {
+ throw new NullPointerException("mgfName == null");
+ } else if (pSrc == null) {
+ throw new NullPointerException("pSrc == null");
}
this.mdName = mdName;
this.mgfName = mgfName;
diff --git a/luni/src/main/java/javax/crypto/spec/PSource.java b/luni/src/main/java/javax/crypto/spec/PSource.java
index 0efa1e9..f644316 100644
--- a/luni/src/main/java/javax/crypto/spec/PSource.java
+++ b/luni/src/main/java/javax/crypto/spec/PSource.java
@@ -40,7 +40,7 @@ public class PSource {
*/
protected PSource(String pSrcName) {
if (pSrcName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pSrcName == null");
}
this.pSrcName = pSrcName;
}
@@ -85,7 +85,7 @@ public class PSource {
public PSpecified(byte[] p) {
super("PSpecified");
if (p == null) {
- throw new NullPointerException();
+ throw new NullPointerException("p == null");
}
//TODO: It is unknown which name should be used!
//super("");
diff --git a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
index 82ce8a1..0b3db61 100644
--- a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
+++ b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
@@ -68,7 +68,7 @@ public class KeyManagerFactory {
public static final KeyManagerFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyManagerFactory((KeyManagerFactorySpi) sap.spi, sap.provider, algorithm);
@@ -127,7 +127,7 @@ public class KeyManagerFactory {
throw new IllegalArgumentException("Provider is null");
}
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyManagerFactory((KeyManagerFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/net/ssl/SSLContext.java b/luni/src/main/java/javax/net/ssl/SSLContext.java
index 9097bb9..a59f301 100644
--- a/luni/src/main/java/javax/net/ssl/SSLContext.java
+++ b/luni/src/main/java/javax/net/ssl/SSLContext.java
@@ -93,7 +93,7 @@ public class SSLContext {
*/
public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException {
if (protocol == null) {
- throw new NullPointerException("protocol is null");
+ throw new NullPointerException("protocol == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(protocol, null);
return new SSLContext((SSLContextSpi) sap.spi, sap.provider, protocol);
@@ -154,7 +154,7 @@ public class SSLContext {
throw new IllegalArgumentException("provider is null");
}
if (protocol == null) {
- throw new NullPointerException("protocol is null");
+ throw new NullPointerException("protocol == null");
}
Object spi = ENGINE.getInstance(protocol, provider, null);
return new SSLContext((SSLContextSpi) spi, provider, protocol);
diff --git a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
index bf3bf8c..be9db06 100644
--- a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
+++ b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
@@ -67,7 +67,7 @@ public class TrustManagerFactory {
public static final TrustManagerFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new TrustManagerFactory((TrustManagerFactorySpi) sap.spi, sap.provider, algorithm);
@@ -126,7 +126,7 @@ public class TrustManagerFactory {
throw new IllegalArgumentException("Provider is null");
}
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new TrustManagerFactory((TrustManagerFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/security/auth/Subject.java b/luni/src/main/java/javax/security/auth/Subject.java
index a958484..6c9c036 100644
--- a/luni/src/main/java/javax/security/auth/Subject.java
+++ b/luni/src/main/java/javax/security/auth/Subject.java
@@ -116,8 +116,12 @@ public final class Subject implements Serializable {
public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals,
Set<?> pubCredentials, Set<?> privCredentials) {
- if (subjPrincipals == null || pubCredentials == null || privCredentials == null) {
- throw new NullPointerException();
+ if (subjPrincipals == null) {
+ throw new NullPointerException("subjPrincipals == null");
+ } else if (pubCredentials == null) {
+ throw new NullPointerException("pubCredentials == null");
+ } else if (privCredentials == null) {
+ throw new NullPointerException("privCredentials == null");
}
principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals);
@@ -467,7 +471,7 @@ public final class Subject implements Serializable {
*/
public static Subject getSubject(final AccessControlContext context) {
if (context == null) {
- throw new NullPointerException("AccessControlContext cannot be null");
+ throw new NullPointerException("context == null");
}
PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() {
public DomainCombiner run() {
@@ -554,7 +558,7 @@ public final class Subject implements Serializable {
private void verifyElement(Object o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) {
throw new IllegalArgumentException("Element is not instance of java.security.Principal");
@@ -607,7 +611,7 @@ public final class Subject implements Serializable {
public boolean retainAll(Collection<?> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
return super.retainAll(c);
}
@@ -624,7 +628,7 @@ public final class Subject implements Serializable {
protected final <E> Set<E> get(final Class<E> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
AbstractSet<E> s = new AbstractSet<E>() {
@@ -652,7 +656,7 @@ public final class Subject implements Serializable {
public boolean retainAll(Collection<?> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
return super.retainAll(c);
}
diff --git a/luni/src/main/java/javax/security/auth/x500/X500Principal.java b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
index e6453e9..cedebe0 100644
--- a/luni/src/main/java/javax/security/auth/x500/X500Principal.java
+++ b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
@@ -123,7 +123,7 @@ public final class X500Principal implements Serializable, Principal {
*/
public X500Principal(String name) {
if (name == null) {
- throw new NullPointerException("Name cannot be null");
+ throw new NullPointerException("name == null");
}
try {
dn = new Name(name);
@@ -134,7 +134,7 @@ public final class X500Principal implements Serializable, Principal {
public X500Principal(String name, Map<String,String> keywordMap){
if (name == null) {
- throw new NullPointerException("Name cannot be null");
+ throw new NullPointerException("name == null");
}
try {
dn = new Name(substituteNameFromMap(name, keywordMap));
diff --git a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
index 6a89dae..68291b6 100644
--- a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
+++ b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
@@ -326,7 +326,7 @@ public abstract class DatatypeFactory {
*/
public Duration newDurationDayTime(final String lexicalRepresentation) {
if (lexicalRepresentation == null) {
- throw new NullPointerException("The lexical representation cannot be null.");
+ throw new NullPointerException("lexicalRepresentation == null");
}
// The lexical representation must match the pattern [^YM]*(T.*)?
int pos = lexicalRepresentation.indexOf('T');
@@ -539,7 +539,7 @@ public abstract class DatatypeFactory {
*/
public Duration newDurationYearMonth(final String lexicalRepresentation) {
if (lexicalRepresentation == null) {
- throw new NullPointerException("The lexical representation cannot be null.");
+ throw new NullPointerException("lexicalRepresentation == null");
}
// The lexical representation must match the pattern [^DT]*.
int length = lexicalRepresentation.length();
diff --git a/luni/src/main/java/javax/xml/datatype/Duration.java b/luni/src/main/java/javax/xml/datatype/Duration.java
index 8121d36..fcdd4c5 100644
--- a/luni/src/main/java/javax/xml/datatype/Duration.java
+++ b/luni/src/main/java/javax/xml/datatype/Duration.java
@@ -552,11 +552,7 @@ public abstract class Duration {
// check data parameter
if (date == null) {
- throw new NullPointerException(
- "Cannot call "
- + this.getClass().getName()
- + "#addTo(Date date) with date == null."
- );
+ throw new NullPointerException("date == null");
}
Calendar cal = new GregorianCalendar();
diff --git a/luni/src/main/java/javax/xml/namespace/QName.java b/luni/src/main/java/javax/xml/namespace/QName.java
index f748b64..a82487f 100644
--- a/luni/src/main/java/javax/xml/namespace/QName.java
+++ b/luni/src/main/java/javax/xml/namespace/QName.java
@@ -22,8 +22,6 @@ package javax.xml.namespace;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import javax.xml.XMLConstants;
/**
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactory.java b/luni/src/main/java/javax/xml/validation/SchemaFactory.java
index 23e4798..2018067 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactory.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactory.java
@@ -203,8 +203,10 @@ public abstract class SchemaFactory {
*/
public static SchemaFactory newInstance(String schemaLanguage, String factoryClassName,
ClassLoader classLoader) {
- if (schemaLanguage == null || factoryClassName == null) {
- throw new NullPointerException("schemaLanguage == null || factoryClassName == null");
+ if (schemaLanguage == null) {
+ throw new NullPointerException("schemaLanguage == null");
+ } else if (factoryClassName == null) {
+ throw new NullPointerException("factoryClassName == null");
}
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
@@ -265,7 +267,7 @@ public abstract class SchemaFactory {
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -313,7 +315,7 @@ public abstract class SchemaFactory {
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -340,7 +342,7 @@ public abstract class SchemaFactory {
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -371,7 +373,7 @@ public abstract class SchemaFactory {
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
index 3a6cb83..636777c 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
@@ -131,7 +131,9 @@ final class SchemaFactoryFinder {
* If the <tt>schemaLanguage</tt> parameter is null.
*/
public SchemaFactory newFactory(String schemaLanguage) {
- if(schemaLanguage==null) throw new NullPointerException();
+ if (schemaLanguage == null) {
+ throw new NullPointerException("schemaLanguage == null");
+ }
SchemaFactory f = _newFactory(schemaLanguage);
if (debug) {
if (f != null) {
diff --git a/luni/src/main/java/javax/xml/validation/Validator.java b/luni/src/main/java/javax/xml/validation/Validator.java
index b4ee1ca..ea7908a 100644
--- a/luni/src/main/java/javax/xml/validation/Validator.java
+++ b/luni/src/main/java/javax/xml/validation/Validator.java
@@ -339,7 +339,9 @@ public abstract class Validator {
* @see #setFeature(String, boolean)
*/
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -372,7 +374,9 @@ public abstract class Validator {
* @see #getFeature(String)
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -400,7 +404,9 @@ public abstract class Validator {
* When the name parameter is null.
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -431,7 +437,9 @@ public abstract class Validator {
* @see #setProperty(String, Object)
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
}
diff --git a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
index 9606193..2b621ff 100644
--- a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
+++ b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
@@ -347,8 +347,9 @@ public abstract class ValidatorHandler implements ContentHandler {
* @see #setFeature(String, boolean)
*/
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -381,8 +382,9 @@ public abstract class ValidatorHandler implements ContentHandler {
* @see #getFeature(String)
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -411,8 +413,9 @@ public abstract class ValidatorHandler implements ContentHandler {
* When the name parameter is null.
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -443,8 +446,9 @@ public abstract class ValidatorHandler implements ContentHandler {
* @see #setProperty(String, Object)
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
}
diff --git a/luni/src/main/java/javax/xml/xpath/XPathException.java b/luni/src/main/java/javax/xml/xpath/XPathException.java
index 8db369b..376d477 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathException.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathException.java
@@ -48,8 +48,8 @@ public class XPathException extends Exception {
*/
public XPathException(String message) {
super(message);
- if ( message == null ) {
- throw new NullPointerException ( "message can't be null");
+ if (message == null) {
+ throw new NullPointerException("message == null");
}
this.cause = null;
}
@@ -66,8 +66,8 @@ public class XPathException extends Exception {
public XPathException(Throwable cause) {
super(cause == null ? null : cause.toString());
this.cause = cause;
- if ( cause == null ) {
- throw new NullPointerException ( "cause can't be null");
+ if (cause == null) {
+ throw new NullPointerException("cause == null");
}
}
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactory.java b/luni/src/main/java/javax/xml/xpath/XPathFactory.java
index 8b1c1fa..57f2195 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactory.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactory.java
@@ -133,9 +133,7 @@ public abstract class XPathFactory {
public static final XPathFactory newInstance(final String uri)
throws XPathFactoryConfigurationException {
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException("uri == null");
}
if (uri.length() == 0) {
throw new IllegalArgumentException(
@@ -167,9 +165,7 @@ public abstract class XPathFactory {
public static XPathFactory newInstance(String uri, String factoryClassName,
ClassLoader classLoader) throws XPathFactoryConfigurationException {
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException("uri == null");
}
if (uri.length() == 0) {
throw new IllegalArgumentException(
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
index 652a1d8..0113e7d 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
@@ -126,7 +126,9 @@ final class XPathFactoryFinder {
* If the parameter is null.
*/
public XPathFactory newFactory(String uri) {
- if(uri==null) throw new NullPointerException();
+ if (uri == null) {
+ throw new NullPointerException("uri == null");
+ }
XPathFactory f = _newFactory(uri);
if (debug) {
if (f != null) {
diff --git a/luni/src/main/java/javax/xml/xpath/package.html b/luni/src/main/java/javax/xml/xpath/package.html
index edeb448..202a29c 100644
--- a/luni/src/main/java/javax/xml/xpath/package.html
+++ b/luni/src/main/java/javax/xml/xpath/package.html
@@ -165,7 +165,7 @@ or more nodes from an XML document:</p>
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "/widgets/widget";
InputSource inputSource = new InputSource("widgets.xml");
-NodeSet nodes = (NodeSet) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
+NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
</pre>
<h3>XPath Expressions and Types</h3>
diff --git a/luni/src/main/java/libcore/icu/ErrorCode.java b/luni/src/main/java/libcore/icu/ErrorCode.java
deleted file mode 100644
index c093af2..0000000
--- a/luni/src/main/java/libcore/icu/ErrorCode.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
-******************************************************************************
-* Copyright (C) 1996-2005, International Business Machines Corporation and *
-* others. All Rights Reserved. *
-******************************************************************************
-*
-******************************************************************************
-*/
-
-package libcore.icu;
-
-/**
- * Error exception class mapping ICU error codes of the enum UErrorCode
- * @author syn wee quek
-*/
-public final class ErrorCode extends Exception {
- public static boolean isFailure(int error) {
- return error > U_ZERO_ERROR && error < U_ERROR_LIMIT;
- }
-
- public static RuntimeException throwException(int error) {
- if (error <= U_ZERO_ERROR && error >= U_ERROR_LIMIT) {
- return null;
- }
- switch (error) {
- case U_ILLEGAL_ARGUMENT_ERROR:
- return new IllegalArgumentException(ERROR_NAMES[error]);
- case U_INDEX_OUTOFBOUNDS_ERROR:
- case U_BUFFER_OVERFLOW_ERROR:
- return new ArrayIndexOutOfBoundsException(ERROR_NAMES[error]);
- case U_UNSUPPORTED_ERROR:
- return new UnsupportedOperationException(ERROR_NAMES[error]);
- }
- throw new RuntimeException(ERROR_NAMES[error]);
- }
-
- // The errors needed by our CharsetDecoderICU/CharsetEncoderICU.
- public static final int U_ZERO_ERROR = 0;
- private static final int U_ILLEGAL_ARGUMENT_ERROR = 1;
- private static final int U_INDEX_OUTOFBOUNDS_ERROR = 8;
- public static final int U_INVALID_CHAR_FOUND = 10;
- public static final int U_TRUNCATED_CHAR_FOUND = 11;
- public static final int U_ILLEGAL_CHAR_FOUND = 12;
- public static final int U_BUFFER_OVERFLOW_ERROR = 15;
- private static final int U_UNSUPPORTED_ERROR = 16;
- private static final int U_ERROR_LIMIT = 21;
-
- // TODO: this list is incomplete; get these from native code!
- private static final String ERROR_NAMES[] = {
- "U_ZERO_ERROR",
- "U_ILLEGAL_ARGUMENT_ERROR",
- "U_MISSING_RESOURCE_ERROR",
- "U_INVALID_FORMAT_ERROR",
- "U_FILE_ACCESS_ERROR",
- "U_INTERNAL_PROGRAM_ERROR",
- "U_MESSAGE_PARSE_ERROR",
- "U_MEMORY_ALLOCATION_ERROR",
- "U_INDEX_OUTOFBOUNDS_ERROR",
- "U_PARSE_ERROR",
- "U_INVALID_CHAR_FOUND",
- "U_TRUNCATED_CHAR_FOUND",
- "U_ILLEGAL_CHAR_FOUND",
- "U_INVALID_TABLE_FORMAT",
- "U_INVALID_TABLE_FILE",
- "U_BUFFER_OVERFLOW_ERROR",
- "U_UNSUPPORTED_ERROR",
- "U_RESOURCE_TYPE_MISMATCH",
- "U_ILLEGAL_ESCAPE_SEQUENCE",
- "U_UNSUPPORTED_ESCAPE_SEQUENCE"
- };
-}
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 7f3303e..9984414 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -142,6 +142,19 @@ public final class ICU {
public static native String toLowerCase(String s, String localeName);
public static native String toUpperCase(String s, String localeName);
+ // --- Errors.
+
+ // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU.
+ public static final int U_ZERO_ERROR = 0;
+ public static final int U_INVALID_CHAR_FOUND = 10;
+ public static final int U_TRUNCATED_CHAR_FOUND = 11;
+ public static final int U_ILLEGAL_CHAR_FOUND = 12;
+ public static final int U_BUFFER_OVERFLOW_ERROR = 15;
+
+ public static boolean U_FAILURE(int error) {
+ return error > U_ZERO_ERROR;
+ }
+
// --- Native methods accessing ICU's database.
private static native String[] getAvailableBreakIteratorLocalesNative();
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index cb9c880..8ec2294 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -20,6 +20,7 @@ import java.text.DateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
+import libcore.util.Objects;
/**
* Passes locale-specific from ICU native code to Java.
@@ -46,18 +47,27 @@ public final class LocaleData {
public Integer minimalDaysInFirstWeek;
// Used by DateFormatSymbols.
- public String[] amPm;
- public String[] eras;
-
- public String[] longMonthNames;
- public String[] shortMonthNames;
- public String[] longStandAloneMonthNames;
- public String[] shortStandAloneMonthNames;
-
- public String[] longWeekdayNames;
- public String[] shortWeekdayNames;
- public String[] longStandAloneWeekdayNames;
- public String[] shortStandAloneWeekdayNames;
+ public String[] amPm; // "AM", "PM".
+ public String[] eras; // "BC", "AD".
+
+ public String[] longMonthNames; // "January", ...
+ public String[] shortMonthNames; // "Jan", ...
+ public String[] tinyMonthNames; // "J", ...
+ public String[] longStandAloneMonthNames; // "January", ...
+ public String[] shortStandAloneMonthNames; // "Jan", ...
+ public String[] tinyStandAloneMonthNames; // "J", ...
+
+ public String[] longWeekdayNames; // "Sunday", ...
+ public String[] shortWeekdayNames; // "Sun", ...
+ public String[] tinyWeekdayNames; // "S", ...
+ public String[] longStandAloneWeekdayNames; // "Sunday", ...
+ public String[] shortStandAloneWeekdayNames; // "Sun", ...
+ public String[] tinyStandAloneWeekdayNames; // "S", ...
+
+ // Used by frameworks/base DateSorter and DateUtils.
+ public String yesterday; // "Yesterday".
+ public String today; // "Today".
+ public String tomorrow; // "Tomorrow".
public String fullTimeFormat;
public String longTimeFormat;
@@ -120,44 +130,7 @@ public final class LocaleData {
}
@Override public String toString() {
- return "LocaleData[" +
- "firstDayOfWeek=" + firstDayOfWeek + "," +
- "minimalDaysInFirstWeek=" + minimalDaysInFirstWeek + "," +
- "amPm=" + Arrays.toString(amPm) + "," +
- "eras=" + Arrays.toString(eras) + "," +
- "longMonthNames=" + Arrays.toString(longMonthNames) + "," +
- "shortMonthNames=" + Arrays.toString(shortMonthNames) + "," +
- "longStandAloneMonthNames=" + Arrays.toString(longStandAloneMonthNames) + "," +
- "shortStandAloneMonthNames=" + Arrays.toString(shortStandAloneMonthNames) + "," +
- "longWeekdayNames=" + Arrays.toString(longWeekdayNames) + "," +
- "shortWeekdayNames=" + Arrays.toString(shortWeekdayNames) + "," +
- "longStandAloneWeekdayNames=" + Arrays.toString(longStandAloneWeekdayNames) + "," +
- "shortStandAloneWeekdayNames=" + Arrays.toString(shortStandAloneWeekdayNames) + "," +
- "fullTimeFormat=" + fullTimeFormat + "," +
- "longTimeFormat=" + longTimeFormat + "," +
- "mediumTimeFormat=" + mediumTimeFormat + "," +
- "shortTimeFormat=" + shortTimeFormat + "," +
- "fullDateFormat=" + fullDateFormat + "," +
- "longDateFormat=" + longDateFormat + "," +
- "mediumDateFormat=" + mediumDateFormat + "," +
- "shortDateFormat=" + shortDateFormat + "," +
- "zeroDigit=" + zeroDigit + "," +
- "decimalSeparator=" + decimalSeparator + "," +
- "groupingSeparator=" + groupingSeparator + "," +
- "patternSeparator=" + patternSeparator + "," +
- "percent=" + percent + "," +
- "perMill=" + perMill + "," +
- "monetarySeparator=" + monetarySeparator + "," +
- "minusSign=" + minusSign + "," +
- "exponentSeparator=" + exponentSeparator + "," +
- "infinity=" + infinity + "," +
- "NaN=" + NaN + "," +
- "currencySymbol=" + currencySymbol + "," +
- "internationalCurrencySymbol=" + internationalCurrencySymbol + "," +
- "numberPattern=" + numberPattern + "," +
- "integerPattern=" + integerPattern + "," +
- "currencyPattern=" + currencyPattern + "," +
- "percentPattern=" + percentPattern + "]";
+ return Objects.toString(this);
}
public String getDateFormat(int style) {
diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java
index 2d8630c..e18f483 100644
--- a/luni/src/main/java/libcore/icu/NativeConverter.java
+++ b/luni/src/main/java/libcore/icu/NativeConverter.java
@@ -54,19 +54,19 @@ public final class NativeConverter {
}
}
- public static int setCallbackDecode(long converterHandle, CharsetDecoder decoder) {
- return setCallbackDecode(converterHandle,
- translateCodingErrorAction(decoder.malformedInputAction()),
- translateCodingErrorAction(decoder.unmappableCharacterAction()),
- decoder.replacement());
+ public static void setCallbackDecode(long converterHandle, CharsetDecoder decoder) {
+ setCallbackDecode(converterHandle,
+ translateCodingErrorAction(decoder.malformedInputAction()),
+ translateCodingErrorAction(decoder.unmappableCharacterAction()),
+ decoder.replacement());
}
- private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars);
+ private static native void setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars);
- public static int setCallbackEncode(long converterHandle, CharsetEncoder encoder) {
- return setCallbackEncode(converterHandle,
- translateCodingErrorAction(encoder.malformedInputAction()),
- translateCodingErrorAction(encoder.unmappableCharacterAction()),
- encoder.replacement());
+ public static void setCallbackEncode(long converterHandle, CharsetEncoder encoder) {
+ setCallbackEncode(converterHandle,
+ translateCodingErrorAction(encoder.malformedInputAction()),
+ translateCodingErrorAction(encoder.unmappableCharacterAction()),
+ encoder.replacement());
}
- private static native int setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes);
+ private static native void setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes);
}
diff --git a/luni/src/main/java/libcore/icu/NativeIDN.java b/luni/src/main/java/libcore/icu/NativeIDN.java
index 9bf5cb1..db93379 100644
--- a/luni/src/main/java/libcore/icu/NativeIDN.java
+++ b/luni/src/main/java/libcore/icu/NativeIDN.java
@@ -34,7 +34,7 @@ public final class NativeIDN {
private static String convert(String s, int flags, boolean toAscii) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
return convertImpl(s, flags, toAscii);
}
diff --git a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
index d036c98..4221fe6 100644
--- a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
+++ b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
@@ -46,7 +46,7 @@ public final class RuleBasedCollatorICU implements Cloneable {
public RuleBasedCollatorICU(String rules) throws ParseException {
if (rules == null) {
- throw new NullPointerException();
+ throw new NullPointerException("rules == null");
}
address = NativeCollation.openCollatorFromRules(rules, VALUE_OFF, VALUE_DEFAULT_STRENGTH);
}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 4f2858d..c61a3cf 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -50,7 +50,7 @@ public class BlockGuardOs extends ForwardingOs {
}
}
- @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException {
+ @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return tagSocket(os.accept(fd, peerAddress));
}
@@ -80,7 +80,7 @@ public class BlockGuardOs extends ForwardingOs {
return linger.isOn() && linger.l_linger > 0;
}
- @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException {
+ @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
os.connect(fd, address, port);
}
@@ -154,22 +154,22 @@ public class BlockGuardOs extends ForwardingOs {
return os.readv(fd, buffers, offsets, byteCounts);
}
- @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.recvfrom(fd, buffer, flags, srcAddress);
}
- @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
- @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.sendto(fd, buffer, flags, inetAddress, port);
}
- @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
// We permit datagrams without hostname lookups.
if (inetAddress != null) {
BlockGuard.getThreadPolicy().onNetwork();
@@ -181,6 +181,12 @@ public class BlockGuardOs extends ForwardingOs {
return tagSocket(os.socket(domain, type, protocol));
}
+ @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException {
+ os.socketpair(domain, type, protocol, fd1, fd2);
+ tagSocket(fd1);
+ tagSocket(fd2);
+ }
+
@Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, buffer);
diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java
index fa26624..8338983 100644
--- a/luni/src/main/java/libcore/io/DiskLruCache.java
+++ b/luni/src/main/java/libcore/io/DiskLruCache.java
@@ -458,9 +458,14 @@ public final class DiskLruCache implements Closeable {
// if this edit is creating the entry for the first time, every index must have a value
if (success && !entry.readable) {
for (int i = 0; i < valueCount; i++) {
+ if (!editor.written[i]) {
+ editor.abort();
+ throw new IllegalStateException("Newly created entry didn't create value for index " + i);
+ }
if (!entry.getDirtyFile(i).exists()) {
editor.abort();
- throw new IllegalStateException("edit didn't create file " + i);
+ System.logW("DiskLruCache: Newly created entry doesn't have file for index " + i);
+ return;
}
}
}
@@ -659,10 +664,12 @@ public final class DiskLruCache implements Closeable {
*/
public final class Editor {
private final Entry entry;
+ private final boolean[] written;
private boolean hasErrors;
private Editor(Entry entry) {
this.entry = entry;
+ this.written = (entry.readable) ? null : new boolean[valueCount];
}
/**
@@ -702,6 +709,9 @@ public final class DiskLruCache implements Closeable {
if (entry.currentEditor != this) {
throw new IllegalStateException();
}
+ if (!entry.readable) {
+ written[index] = true;
+ }
return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
}
}
diff --git a/luni/src/main/java/libcore/io/DropBox.java b/luni/src/main/java/libcore/io/DropBox.java
new file mode 100644
index 0000000..cf88106
--- /dev/null
+++ b/luni/src/main/java/libcore/io/DropBox.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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 libcore.io;
+
+public final class DropBox {
+
+ /**
+ * Hook for customizing how events are reported.
+ */
+ private static volatile Reporter REPORTER = new DefaultReporter();
+
+ /**
+ * Used to replace default Reporter for logging events. Must be non-null.
+ */
+ public static void setReporter(Reporter reporter) {
+ if (reporter == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ REPORTER = reporter;
+ }
+
+ /**
+ * Returns non-null Reporter.
+ */
+ public static Reporter getReporter() {
+ return REPORTER;
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ */
+ public static interface Reporter {
+ public void addData(String tag, byte[] data, int flags);
+ public void addText(String tag, String data);
+ }
+
+ /**
+ * Default Reporter which reports events to the log.
+ */
+ private static final class DefaultReporter implements Reporter {
+
+ public void addData(String tag, byte[] data, int flags) {
+ System.out.println(tag + ": " + Base64.encode(data));
+ }
+
+ public void addText(String tag, String data) {
+ System.out.println(tag + ": " + data);
+ }
+ }
+
+ public static void addData(String tag, byte[] data, int flags) {
+ getReporter().addData(tag, data, flags);
+ }
+
+ public static void addText(String tag, String data) {
+ getReporter().addText(tag, data);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/EventLogger.java b/luni/src/main/java/libcore/io/EventLogger.java
new file mode 100644
index 0000000..9709cc9
--- /dev/null
+++ b/luni/src/main/java/libcore/io/EventLogger.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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 libcore.io;
+
+public final class EventLogger {
+
+ /**
+ * Hook for customizing how events are reported.
+ */
+ private static volatile Reporter REPORTER = new DefaultReporter();
+
+ /**
+ * Used to replace default Reporter for logging events. Must be non-null.
+ */
+ public static void setReporter(Reporter reporter) {
+ if (reporter == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ REPORTER = reporter;
+ }
+
+ /**
+ * Returns non-null Reporter.
+ */
+ public static Reporter getReporter() {
+ return REPORTER;
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ */
+ public static interface Reporter {
+ public void report (int code, Object... list);
+ }
+
+ /**
+ * Default Reporter which reports events to the log.
+ */
+ private static final class DefaultReporter implements Reporter {
+ @Override
+ public void report (int code, Object... list) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(code);
+ for (Object o : list) {
+ sb.append(",");
+ sb.append(o.toString());
+ }
+ System.out.println(sb);
+ }
+ }
+
+ public static void writeEvent(int code, Object... list) {
+ getReporter().report(code, list);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 2c8e562..ee26d04 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -20,6 +20,7 @@ import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
import libcore.util.MutableLong;
@@ -34,15 +35,18 @@ public class ForwardingOs implements Os {
this.os = os;
}
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { return os.accept(fd, peerAddress); }
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
- public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); }
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
+ public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
- public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.connect(fd, address, port); }
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); }
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
public String[] environ() { return os.environ(); }
+ public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); }
+ public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); }
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); }
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException { return os.fcntlFlock(fd, cmd, arg); }
@@ -75,6 +79,7 @@ public class ForwardingOs implements Os {
public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); }
public boolean isatty(FileDescriptor fd) { return os.isatty(fd); }
public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); }
+ public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); }
public void listen(FileDescriptor fd, int backlog) throws ErrnoException { os.listen(fd, backlog); }
public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return os.lseek(fd, offset, whence); }
public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
@@ -95,16 +100,17 @@ public class ForwardingOs implements Os {
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, srcAddress); }
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
public void remove(String path) throws ErrnoException { os.remove(path); }
public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, buffer, flags, inetAddress, port); }
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
+ public int setsid() throws ErrnoException { return os.setsid(); }
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); }
@@ -115,11 +121,14 @@ public class ForwardingOs implements Os {
public void setuid(int uid) throws ErrnoException { os.setuid(uid); }
public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); }
public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); }
+ public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); }
public StructStat stat(String path) throws ErrnoException { return os.stat(path); }
public StructStatFs statfs(String path) throws ErrnoException { return os.statfs(path); }
public String strerror(int errno) { return os.strerror(errno); }
public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); }
public long sysconf(int name) { return os.sysconf(name); }
+ public void tcdrain(FileDescriptor fd) throws ErrnoException { os.tcdrain(fd); }
+ public int umask(int mask) { return os.umask(mask); }
public StructUtsname uname() { return os.uname(); }
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index d637b67..1f42497 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -20,20 +20,24 @@ import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
import libcore.util.MutableLong;
public interface Os {
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
public boolean access(String path, int mode) throws ErrnoException;
- public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public void chmod(String path, int mode) throws ErrnoException;
+ public void chown(String path, int uid, int gid) throws ErrnoException;
public void close(FileDescriptor fd) throws ErrnoException;
- public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public String[] environ();
+ public void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
+ public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException;
@@ -67,6 +71,7 @@ public interface Os {
public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
public boolean isatty(FileDescriptor fd);
public void kill(int pid, int signal) throws ErrnoException;
+ public void lchown(String path, int uid, int gid) throws ErrnoException;
public void listen(FileDescriptor fd, int backlog) throws ErrnoException;
public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException;
public StructStat lstat(String path) throws ErrnoException;
@@ -88,16 +93,17 @@ public interface Os {
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public void remove(String path) throws ErrnoException;
public void rename(String oldPath, String newPath) throws ErrnoException;
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException;
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
public void setegid(int egid) throws ErrnoException;
public void seteuid(int euid) throws ErrnoException;
public void setgid(int gid) throws ErrnoException;
+ public int setsid() throws ErrnoException;
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -108,12 +114,15 @@ public interface Os {
public void setuid(int uid) throws ErrnoException;
public void shutdown(FileDescriptor fd, int how) throws ErrnoException;
public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
+ public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException;
public StructStat stat(String path) throws ErrnoException;
/* TODO: replace statfs with statvfs. */
public StructStatFs statfs(String path) throws ErrnoException;
public String strerror(int errno);
public void symlink(String oldPath, String newPath) throws ErrnoException;
public long sysconf(int name);
+ public void tcdrain(FileDescriptor fd) throws ErrnoException;
+ public int umask(int mask);
public StructUtsname uname();
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java
index 68a165c..9f381f5 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/libcore/io/OsConstants.java
@@ -224,6 +224,7 @@ public final class OsConstants {
public static final int O_CREAT = placeholder();
public static final int O_EXCL = placeholder();
public static final int O_NOCTTY = placeholder();
+ public static final int O_NOFOLLOW = placeholder();
public static final int O_NONBLOCK = placeholder();
public static final int O_RDONLY = placeholder();
public static final int O_RDWR = placeholder();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 7bbf49f..2fb187e 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -20,6 +20,7 @@ import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.NioUtils;
import libcore.util.MutableInt;
@@ -28,15 +29,18 @@ import libcore.util.MutableLong;
public final class Posix implements Os {
Posix() { }
- public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
+ public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
public native boolean access(String path, int mode) throws ErrnoException;
- public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public native void chmod(String path, int mode) throws ErrnoException;
+ public native void chown(String path, int uid, int gid) throws ErrnoException;
public native void close(FileDescriptor fd) throws ErrnoException;
- public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public native String[] environ();
+ public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
+ public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException;
@@ -69,6 +73,7 @@ public final class Posix implements Os {
public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
public native boolean isatty(FileDescriptor fd);
public native void kill(int pid, int signal) throws ErrnoException;
+ public native void lchown(String path, int uid, int gid) throws ErrnoException;
public native void listen(FileDescriptor fd, int backlog) throws ErrnoException;
public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException;
public native StructStat lstat(String path) throws ErrnoException;
@@ -119,36 +124,37 @@ public final class Posix implements Os {
}
private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
} else {
return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress);
}
}
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
- private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+ private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public native void remove(String path) throws ErrnoException;
public native void rename(String oldPath, String newPath) throws ErrnoException;
public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return sendtoBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, inetAddress, port);
} else {
return sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, inetAddress, port);
}
}
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
}
- private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public native void setegid(int egid) throws ErrnoException;
public native void seteuid(int euid) throws ErrnoException;
public native void setgid(int gid) throws ErrnoException;
+ public native int setsid() throws ErrnoException;
public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -159,11 +165,14 @@ public final class Posix implements Os {
public native void setuid(int uid) throws ErrnoException;
public native void shutdown(FileDescriptor fd, int how) throws ErrnoException;
public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
+ public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException;
public native StructStat stat(String path) throws ErrnoException;
public native StructStatFs statfs(String path) throws ErrnoException;
public native String strerror(int errno);
public native void symlink(String oldPath, String newPath) throws ErrnoException;
public native long sysconf(int name);
+ public native void tcdrain(FileDescriptor fd) throws ErrnoException;
+ public native int umask(int mask);
public native StructUtsname uname();
public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
diff --git a/luni/src/main/java/libcore/io/StrictLineReader.java b/luni/src/main/java/libcore/io/StrictLineReader.java
index 5f8d452..36556a0 100644
--- a/luni/src/main/java/libcore/io/StrictLineReader.java
+++ b/luni/src/main/java/libcore/io/StrictLineReader.java
@@ -106,8 +106,10 @@ public class StrictLineReader implements Closeable {
* or the specified charset is not supported.
*/
public StrictLineReader(InputStream in, int capacity, Charset charset) {
- if (in == null || charset == null) {
- throw new NullPointerException();
+ if (in == null) {
+ throw new NullPointerException("in == null");
+ } else if (charset == null) {
+ throw new NullPointerException("charset == null");
}
if (capacity < 0) {
throw new IllegalArgumentException("capacity <= 0");
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index f8038f0..6ea0baf 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -350,6 +350,7 @@ public final class MimeUtils {
add("video/x-ms-wvx", "wvx");
add("video/x-msvideo", "avi");
add("video/x-sgi-movie", "movie");
+ add("video/x-webex", "wrf");
add("x-conference/x-cooltalk", "ice");
add("x-epoc/x-sisx-app", "sisx");
applyOverrides();
diff --git a/luni/src/main/java/libcore/net/UriCodec.java b/luni/src/main/java/libcore/net/UriCodec.java
index bde922b..6624474 100644
--- a/luni/src/main/java/libcore/net/UriCodec.java
+++ b/luni/src/main/java/libcore/net/UriCodec.java
@@ -94,7 +94,7 @@ public abstract class UriCodec {
private void appendEncoded(StringBuilder builder, String s, Charset charset,
boolean isPartiallyEncoded) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
int escapeStart = -1;
diff --git a/luni/src/main/java/libcore/net/http/HttpConnection.java b/luni/src/main/java/libcore/net/http/HttpConnection.java
index 2f92706..4a6e65d 100644
--- a/luni/src/main/java/libcore/net/http/HttpConnection.java
+++ b/luni/src/main/java/libcore/net/http/HttpConnection.java
@@ -199,7 +199,6 @@ final class HttpConnection {
// tlsTolerant mimics Chrome's behavior
if (tlsTolerant && unverifiedSocket instanceof OpenSSLSocketImpl) {
OpenSSLSocketImpl openSslSocket = (OpenSSLSocketImpl) unverifiedSocket;
- openSslSocket.setEnabledCompressionMethods(new String[] { "ZLIB"});
openSslSocket.setUseSessionTickets(true);
openSslSocket.setHostname(address.uriHost);
// use SSLSocketFactory default enabled protocols
diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java
index 3e4b9d3..42c9b96 100644
--- a/luni/src/main/java/libcore/net/http/HttpEngine.java
+++ b/luni/src/main/java/libcore/net/http/HttpEngine.java
@@ -69,10 +69,10 @@ import libcore.util.EmptyArray;
* required, use {@link #automaticallyReleaseConnectionToPool()}.
*/
public class HttpEngine {
- private static final CacheResponse BAD_GATEWAY_RESPONSE = new CacheResponse() {
+ private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() {
@Override public Map<String, List<String>> getHeaders() throws IOException {
Map<String, List<String>> result = new HashMap<String, List<String>>();
- result.put(null, Collections.singletonList("HTTP/1.1 502 Bad Gateway"));
+ result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout"));
return result;
}
@Override public InputStream getBody() throws IOException {
@@ -223,14 +223,15 @@ public class HttpEngine {
/*
* The raw response source may require the network, but the request
* headers may forbid network use. In that case, dispose of the network
- * response and use a BAD_GATEWAY response instead.
+ * response and use a GATEWAY_TIMEOUT response instead, as specified
+ * by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
*/
if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
IoUtils.closeQuietly(cachedResponseBody);
}
this.responseSource = ResponseSource.CACHE;
- this.cacheResponse = BAD_GATEWAY_RESPONSE;
+ this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE;
RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
}
@@ -490,8 +491,15 @@ public class HttpEngine {
reusable = false;
}
- // If the headers specify that the connection shouldn't be reused, don't reuse it.
- if (hasConnectionCloseHeader()) {
+ // If the request specified that the connection shouldn't be reused,
+ // don't reuse it. This advice doesn't apply to CONNECT requests because
+ // the "Connection: close" header goes the origin server, not the proxy.
+ if (requestHeaders.hasConnectionClose() && method != CONNECT) {
+ reusable = false;
+ }
+
+ // If the response specified that the connection shouldn't be reused, don't reuse it.
+ if (responseHeaders != null && responseHeaders.hasConnectionClose()) {
reusable = false;
}
@@ -523,8 +531,13 @@ public class HttpEngine {
/*
* If the response was transparently gzipped, remove the gzip header field
* so clients don't double decompress. http://b/3009828
+ *
+ * Also remove the Content-Length in this case because it contains the length
+ * of the gzipped response. This isn't terribly useful and is dangerous because
+ * clients can query the content length, but not the content encoding.
*/
responseHeaders.stripContentEncoding();
+ responseHeaders.stripContentLength();
responseBodyIn = new GZIPInputStream(transferStream);
} else {
responseBodyIn = transferStream;
@@ -758,11 +771,6 @@ public class HttpEngine {
return agent != null ? agent : ("Java" + System.getProperty("java.version"));
}
- private boolean hasConnectionCloseHeader() {
- return (responseHeaders != null && responseHeaders.hasConnectionClose())
- || requestHeaders.hasConnectionClose();
- }
-
protected final String getOriginAddress(URL url) {
int port = url.getPort();
String result = url.getHost();
diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
index db036b6..1a9dfd1 100644
--- a/luni/src/main/java/libcore/net/http/HttpResponseCache.java
+++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
@@ -426,7 +426,7 @@ public final class HttpResponseCache extends ResponseCache implements ExtendedRe
}
public void writeTo(DiskLruCache.Editor editor) throws IOException {
- OutputStream out = editor.newOutputStream(0);
+ OutputStream out = editor.newOutputStream(ENTRY_METADATA);
Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8));
writer.write(uri + '\n');
diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
index a59df55..260a9ad 100644
--- a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
@@ -36,6 +36,7 @@ import java.security.Permission;
import java.util.List;
import java.util.Map;
import libcore.io.Base64;
+import libcore.io.IoUtils;
/**
* This implementation uses HttpEngine to send requests and receive responses.
@@ -87,6 +88,14 @@ class HttpURLConnectionImpl extends HttpURLConnection {
@Override public final void disconnect() {
// Calling disconnect() before a connection exists should have no effect.
if (httpEngine != null) {
+ // We close the response body here instead of in
+ // HttpEngine.release because that is called when input
+ // has been completely read from the underlying socket.
+ // However the response body can be a GZIPInputStream that
+ // still has unread data.
+ if (httpEngine.hasResponse()) {
+ IoUtils.closeQuietly(httpEngine.getResponseBody());
+ }
httpEngine.release(false);
}
}
diff --git a/luni/src/main/java/libcore/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
index 003b445..c0b4200 100644
--- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java
+++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
@@ -187,6 +187,11 @@ public final class ResponseHeaders {
headers.removeAll("Content-Encoding");
}
+ public void stripContentLength() {
+ contentLength = -1;
+ headers.removeAll("Content-Length");
+ }
+
public boolean isChunked() {
return "chunked".equalsIgnoreCase(transferEncoding);
}
diff --git a/luni/src/main/java/libcore/util/BasicLruCache.java b/luni/src/main/java/libcore/util/BasicLruCache.java
index 13ab3db..75e4a75 100644
--- a/luni/src/main/java/libcore/util/BasicLruCache.java
+++ b/luni/src/main/java/libcore/util/BasicLruCache.java
@@ -43,7 +43,7 @@ public class BasicLruCache<K, V> {
*/
public synchronized final V get(K key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
V result = map.get(key);
@@ -68,8 +68,10 @@ public class BasicLruCache<K, V> {
* no longer cached, it has not been passed to {@link #entryEvicted}.
*/
public synchronized final V put(K key, V value) {
- if (key == null || value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
V previous = map.put(key, value);
diff --git a/luni/src/main/java/libcore/util/Objects.java b/luni/src/main/java/libcore/util/Objects.java
index 7817316..573b973 100644
--- a/luni/src/main/java/libcore/util/Objects.java
+++ b/luni/src/main/java/libcore/util/Objects.java
@@ -16,6 +16,10 @@
package libcore.util;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
public final class Objects {
private Objects() {}
@@ -29,4 +33,64 @@ public final class Objects {
public static int hashCode(Object o) {
return (o == null) ? 0 : o.hashCode();
}
+
+ /**
+ * Returns a string reporting the value of each declared field, via reflection.
+ * Static and transient fields are automatically skipped. Produces output like
+ * "SimpleClassName[integer=1234,string="hello",character='c',intArray=[1,2,3]]".
+ */
+ public static String toString(Object o) {
+ Class<?> c = o.getClass();
+ StringBuilder sb = new StringBuilder();
+ sb.append(c.getSimpleName()).append('[');
+ int i = 0;
+ for (Field f : c.getDeclaredFields()) {
+ if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) {
+ continue;
+ }
+ f.setAccessible(true);
+ try {
+ Object value = f.get(o);
+
+ if (i++ > 0) {
+ sb.append(',');
+ }
+
+ sb.append(f.getName());
+ sb.append('=');
+
+ if (value.getClass().isArray()) {
+ if (value.getClass() == boolean[].class) {
+ sb.append(Arrays.toString((boolean[]) value));
+ } else if (value.getClass() == byte[].class) {
+ sb.append(Arrays.toString((byte[]) value));
+ } else if (value.getClass() == char[].class) {
+ sb.append(Arrays.toString((char[]) value));
+ } else if (value.getClass() == double[].class) {
+ sb.append(Arrays.toString((double[]) value));
+ } else if (value.getClass() == float[].class) {
+ sb.append(Arrays.toString((float[]) value));
+ } else if (value.getClass() == int[].class) {
+ sb.append(Arrays.toString((int[]) value));
+ } else if (value.getClass() == long[].class) {
+ sb.append(Arrays.toString((long[]) value));
+ } else if (value.getClass() == short[].class) {
+ sb.append(Arrays.toString((short[]) value));
+ } else {
+ sb.append(Arrays.toString((Object[]) value));
+ }
+ } else if (value.getClass() == Character.class) {
+ sb.append('\'').append(value).append('\'');
+ } else if (value.getClass() == String.class) {
+ sb.append('"').append(value).append('"');
+ } else {
+ sb.append(value);
+ }
+ } catch (IllegalAccessException unexpected) {
+ throw new AssertionError(unexpected);
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 5a8caf2..ac48b23 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -51,29 +51,46 @@ public final class ZoneInfo extends TimeZone {
private final byte[] mTypes;
private final byte[] mIsDsts;
private final boolean mUseDst;
+ private final int mDstSavings; // Implements TimeZone.getDSTSavings.
- ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtOffsets, byte[] isDsts) {
+ ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
mTransitions = transitions;
- mTypes = type;
+ mTypes = types;
mIsDsts = isDsts;
setID(name);
- // Use the latest non-daylight offset (if any) as the raw offset.
- int lastStd;
- for (lastStd = mTransitions.length - 1; lastStd >= 0; lastStd--) {
- if (mIsDsts[mTypes[lastStd] & 0xff] == 0) {
- break;
+ // Find the latest daylight and standard offsets (if any).
+ int lastStd = 0;
+ boolean haveStd = false;
+ int lastDst = 0;
+ boolean haveDst = false;
+ for (int i = mTransitions.length - 1; (!haveStd || !haveDst) && i >= 0; --i) {
+ int type = mTypes[i] & 0xff;
+ if (!haveStd && mIsDsts[type] == 0) {
+ haveStd = true;
+ lastStd = i;
+ }
+ if (!haveDst && mIsDsts[type] != 0) {
+ haveDst = true;
+ lastDst = i;
}
}
- if (lastStd < 0) {
- lastStd = 0;
- }
+
+ // Use the latest non-daylight offset (if any) as the raw offset.
if (lastStd >= mTypes.length) {
mRawOffset = gmtOffsets[0];
} else {
mRawOffset = gmtOffsets[mTypes[lastStd] & 0xff];
}
+ // Use the latest transition's pair of offsets to compute the DST savings.
+ // This isn't generally useful, but it's exposed by TimeZone.getDSTSavings.
+ if (lastDst >= mTypes.length) {
+ mDstSavings = 0;
+ } else {
+ mDstSavings = Math.abs(gmtOffsets[mTypes[lastStd] & 0xff] - gmtOffsets[mTypes[lastDst] & 0xff]) * 1000;
+ }
+
// Cache the oldest known raw offset, in case we're asked about times that predate our
// transition data.
int firstStd = -1;
@@ -111,6 +128,7 @@ public final class ZoneInfo extends TimeZone {
}
mUseDst = usesDst;
+ // tzdata uses seconds, but Java uses milliseconds.
mRawOffset *= 1000;
mEarliestRawOffset = earliestRawOffset * 1000;
}
@@ -185,6 +203,10 @@ public final class ZoneInfo extends TimeZone {
mRawOffset = off;
}
+ @Override public int getDSTSavings() {
+ return mUseDst ? mDstSavings: 0;
+ }
+
@Override public boolean useDaylightTime() {
return mUseDst;
}
@@ -235,7 +257,7 @@ public final class ZoneInfo extends TimeZone {
StringBuilder sb = new StringBuilder();
// First the basics...
sb.append(getClass().getName() + "[" + getID() + ",mRawOffset=" + mRawOffset +
- ",mUseDst=" + mUseDst + "]");
+ ",mUseDst=" + mUseDst + ",mDstSavings=" + mDstSavings + "]");
// ...followed by a zdump(1)-like description of all our transition data.
sb.append("\n");
Formatter f = new Formatter(sb);
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 69b6784..cc7cc4f 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -180,7 +180,7 @@ public final class ZoneInfoDB {
}
}
- private static TimeZone makeTimeZone(String id) throws IOException {
+ public static TimeZone makeTimeZone(String id) throws IOException {
// Work out where in the big data file this time zone is.
int index = Arrays.binarySearch(ids, id);
if (index < 0) {
@@ -259,17 +259,6 @@ public final class ZoneInfoDB {
}
}
- public static TimeZone getTimeZone(String id) {
- if (id == null) {
- return null;
- }
- try {
- return makeTimeZone(id);
- } catch (IOException ignored) {
- return null;
- }
- }
-
public static String getVersion() {
return VERSION;
}
diff --git a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
index 270f63b..151b403 100644
--- a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
+++ b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
@@ -115,8 +115,10 @@ public class NullCipherSpi extends CipherSpi {
@Override
public int engineUpdate(ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
- if (input == null || output == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (output == null) {
+ throw new NullPointerException("output == null");
}
int result = input.limit() - input.position();
try {
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java b/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java
deleted file mode 100644
index 35e6c62..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.util;
-
-import java.util.AbstractCollection;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-/**
- *
- * Reductive hash with two keys
- *
- */
-public class TwoKeyHashMap<E, K, V> extends AbstractMap<String, V> {
-
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
- static final int DEFAULT_INITIAL_SIZE = 16;
-
- private Set<Map.Entry<String, V>> entrySet;
- private Collection<V> values;
- private int size;
- private int arrSize;
- private int modCount;
-
- private Entry<E, K, V>[] arr;
-
- private float loadFactor;
- int threshold = 0;
-
- /**
- * Constructs an empty HashMap
- */
- public TwoKeyHashMap() {
- this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR);
- }
-
- /**
- * Constructs an empty HashMap
- *
- * @param initialCapacity
- */
- public TwoKeyHashMap(int initialCapacity) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR);
- }
-
- /**
- * Constructs an empty HashMap
- *
- * @param initialCapacity
- * @param initialLoadFactor
- */
- @SuppressWarnings("unchecked")
- public TwoKeyHashMap(int initialCapacity, float initialLoadFactor) {
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("initialCapacity should be >= 0");
- }
- if (initialLoadFactor <= 0) {
- throw new IllegalArgumentException(
- "initialLoadFactor should be > 0");
- }
- loadFactor = initialLoadFactor;
- if (initialCapacity == Integer.MAX_VALUE) {
- initialCapacity--;
- }
- arrSize = initialCapacity > 0 ? initialCapacity : 1;
- threshold = (int) (arrSize * loadFactor);
- arr = new Entry[arrSize + 1];
- }
-
- /**
- * Returns a collection view of the values
- */
- public Collection<V> values() {
- if (values == null) {
- values = new ValuesCollectionImpl();
- }
- return values;
- }
-
- /**
- * Returns a collection view of the mappings
- */
- public Set<Map.Entry<String, V>> entrySet() {
- if (entrySet == null) {
- entrySet = new EntrySetImpl();
- }
- return entrySet;
- }
-
- /**
- * Clears the map
- */
- public void clear() {
- modCount++;
- size = 0;
- Arrays.fill(arr, 0, arr.length, null);
- }
-
- /**
- * Removes the mapping for the keys
- *
- * @param key1
- * @param key2
- * @return
- */
- public V remove(Object key1, Object key2) {
- Entry<E, K, V> e = removeEntry(key1, key2);
- return (e != null) ? e.value : null;
- }
-
- /**
- * Associates the specified value with the specified keys in this map
- *
- * @param key1
- * @param key2
- * @param value
- * @return
- */
- public V put(E key1, K key2, V value) {
- if (key1 == null && key2 == null) {
- int index = arrSize;
- if (arr[index] == null) {
- arr[index] = createEntry(0, null, null, value, null);
- size++;
- modCount++;
- return null;
- } else {
- V oldValue = arr[index].value;
- arr[index].value = value;
- return oldValue;
- }
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
- Entry<E, K, V> e = arr[index];
-
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- V oldValue = e.value;
- e.value = value;
- return oldValue;
- }
- e = e.next;
- }
-
- arr[index] = createEntry(hash, key1, key2, value, arr[index]);
- size++;
- modCount++;
-
- if (size > threshold) {
- rehash();
- }
- return null;
- }
-
- /**
- * Rehash the map
- *
- */
- @SuppressWarnings("unchecked")
- void rehash() {
- int newArrSize = (arrSize + 1) * 2 + 1;
- if (newArrSize < 0) {
- newArrSize = Integer.MAX_VALUE - 1;
- }
- Entry<E, K, V>[] newArr = new Entry[newArrSize + 1];
-
- for (int i = 0; i < arr.length - 1; i++) {
- Entry<E, K, V> entry = arr[i];
- while (entry != null) {
- Entry<E, K, V> next = entry.next;
-
- int newIndex = (entry.hash & 0x7fffffff) % newArrSize;
- entry.next = newArr[newIndex];
- newArr[newIndex] = entry;
-
- entry = next;
- }
- }
- newArr[newArrSize] = arr[arrSize]; // move null entry
- arrSize = newArrSize;
-
- // The maximum array size is reached, increased loadFactor
- // will keep array from further growing
- if (arrSize == Integer.MAX_VALUE) {
- loadFactor *= 10;
- }
- threshold = (int) (arrSize * loadFactor);
- arr = newArr;
- }
-
- /**
- * Returns true if this map contains a mapping for {@code key1} and {@code key2}.
- */
- public boolean containsKey(Object key1, Object key2) {
- return findEntry(key1, key2) != null;
- }
-
- /**
- * Return the value by keys
- *
- * @param key1
- * @param key2
- * @return
- */
- public V get(Object key1, Object key2) {
- Entry<E, K, V> e = findEntry(key1, key2);
- if (e != null) {
- return e.value;
- }
- return null;
- }
-
- /**
- * Returns true if this map contains no key-value mappings
- */
- public boolean isEmpty() {
- return size == 0;
- }
-
- /**
- * Returns the number of mappings
- */
- public int size() {
- return size;
- }
-
- /**
- * Creates new entry
- *
- * @param hashCode
- * @param key1
- * @param key2
- * @param value
- * @param next
- * @return
- */
- Entry<E, K, V> createEntry(int hashCode, E key1, K key2, V value,
- Entry<E, K, V> next) {
- return new Entry<E, K, V>(hashCode, key1, key2, value, next);
- }
-
- /**
- * Creates entries iterator
- *
- * @return
- */
- Iterator<Map.Entry<String, V>> createEntrySetIterator() {
- return new EntryIteratorImpl();
- }
-
- /**
- * Creates values iterator
- *
- * @return
- */
- Iterator<V> createValueCollectionIterator() {
- return new ValueIteratorImpl();
- }
-
- /**
- * Entry implementation for the TwoKeyHashMap class
- *
- */
- public static class Entry<E, K, V> implements Map.Entry<String, V> {
- int hash;
- E key1;
- K key2;
- V value;
- Entry<E, K, V> next;
-
- public Entry(int hash, E key1, K key2, V value, Entry<E, K, V> next) {
- this.hash = hash;
- this.key1 = key1;
- this.key2 = key2;
- this.value = value;
- this.next = next;
- }
-
- public String getKey() {
- return key1.toString() + key2.toString();
- }
-
- public E getKey1() {
- return key1;
- }
-
- public K getKey2() {
- return key2;
- }
-
- public V getValue() {
- return value;
- }
-
- public V setValue(V value) {
- V oldValue = this.value;
- this.value = value;
- return oldValue;
- }
-
- public boolean equals(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
-
- Entry<?, ?, ?> e = (Entry<?, ?, ?>) obj;
- Object getKey1 = e.getKey1();
- Object getKey2 = e.getKey2();
- Object getValue = e.getValue();
- if ((key1 == null && getKey1 != null)
- || (key2 == null && getKey2 != null)
- || (value == null && getValue != null)
- || !key1.equals(e.getKey1()) || !key2.equals(e.getKey2())
- || !value.equals(getValue)) {
- return false;
- }
- return true;
- }
-
- public int hashCode() {
- int hash1 = (key1 == null ? 0 : key1.hashCode());
- int hash2 = (key2 == null ? 0 : key2.hashCode());
- return (hash1 + hash2) ^ (value == null ? 0 : value.hashCode());
- }
-
- }
-
- class EntrySetImpl extends AbstractSet<Map.Entry<String, V>> {
- public int size() {
- return size;
- }
-
- public void clear() {
- TwoKeyHashMap.this.clear();
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public boolean contains(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
-
- Entry<?, ?, ?> entry = (Entry<?, ?, ?>) obj;
- Entry<E, K, V> entry2 = findEntry(entry.getKey1(), entry.getKey2());
- if (entry2 == null) {
- return false;
- }
- Object value = entry.getValue();
- Object value2 = entry2.getValue();
- return value == null ? value2 == null : value.equals(value2);
- }
-
- public boolean remove(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
- return removeEntry(((Entry) obj).getKey1(), ((Entry) obj).getKey2()) != null;
- }
-
- public Iterator<Map.Entry<String, V>> iterator() {
- return createEntrySetIterator();
- }
- }
-
- // Iterates Entries inside the Map
- class EntryIteratorImpl implements Iterator<Map.Entry<String, V>> {
- private int startModCount;
- private boolean found;
- private int curr = -1;
- private int returned_index = -1;
- private Entry<E, K, V> curr_entry;
- private Entry<E, K, V> returned_entry;
-
- EntryIteratorImpl() {
- startModCount = modCount;
- }
-
- public boolean hasNext() {
- if (found) {
- return true;
- }
- if (curr_entry != null) {
- curr_entry = curr_entry.next;
- }
- if (curr_entry == null) {
- for (curr++; curr < arr.length && arr[curr] == null; curr++) {
- }
-
- if (curr < arr.length) {
- curr_entry = arr[curr];
- }
- }
- return found = (curr_entry != null);
- }
-
- public Map.Entry<String, V> next() {
- if (modCount != startModCount) {
- throw new ConcurrentModificationException();
- }
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- found = false;
- returned_index = curr;
- returned_entry = curr_entry;
- return (Map.Entry<String, V>) curr_entry;
- }
-
- public void remove() {
- if (returned_index == -1) {
- throw new IllegalStateException();
- }
-
- if (modCount != startModCount) {
- throw new ConcurrentModificationException();
- }
-
- Entry<E, K, V> p = null;
- Entry<E, K, V> e = arr[returned_index];
- while (e != returned_entry) {
- p = e;
- e = e.next;
- }
- if (p != null) {
- p.next = returned_entry.next;
- } else {
- arr[returned_index] = returned_entry.next;
- }
- size--;
- modCount++;
- startModCount++;
- returned_index = -1;
- }
- }
-
- private final Entry<E, K, V> findEntry(Object key1, Object key2) {
- if (key1 == null && key2 == null) {
- return arr[arrSize];
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
- Entry<E, K, V> e = arr[index];
-
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- return e;
- }
- e = e.next;
- }
- return null;
- }
-
- // Removes entry
- private final Entry<E, K, V> removeEntry(Object key1, Object key2) {
- if (key1 == null && key2 == null) {
- int index = arrSize;
- if (arr[index] != null) {
- Entry<E, K, V> ret = arr[index];
- arr[index] = null;
- size--;
- modCount++;
- return ret;
- }
- return null;
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
-
- Entry<E, K, V> e = arr[index];
- Entry<E, K, V> prev = e;
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- if (prev == e) {
- arr[index] = e.next;
- } else {
- prev.next = e.next;
- }
- size--;
- modCount++;
- return e;
- }
-
- prev = e;
- e = e.next;
- }
- return null;
- }
-
- /**
- * An instance is returned by the values() call.
- */
- class ValuesCollectionImpl extends AbstractCollection<V> {
- public int size() {
- return size;
- }
-
- public void clear() {
- TwoKeyHashMap.this.clear();
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public Iterator<V> iterator() {
- return createValueCollectionIterator();
- }
-
- public boolean contains(Object obj) {
- return containsValue(obj);
- }
- }
-
- class ValueIteratorImpl implements Iterator<V> {
- private EntryIteratorImpl itr;
-
- ValueIteratorImpl() {
- this.itr = new EntryIteratorImpl();
- }
-
- public V next() {
- return itr.next().getValue();
- }
-
- public void remove() {
- itr.remove();
- }
-
- public boolean hasNext() {
- return itr.hasNext();
- }
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/SystemScope.java b/luni/src/main/java/org/apache/harmony/security/SystemScope.java
index 89cf56b..bf4f849 100644
--- a/luni/src/main/java/org/apache/harmony/security/SystemScope.java
+++ b/luni/src/main/java/org/apache/harmony/security/SystemScope.java
@@ -79,7 +79,7 @@ public class SystemScope extends IdentityScope {
*/
public synchronized Identity getIdentity(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
return (Identity) names.get(name);
}
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
index 8a67ac2..f1dd43c 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
@@ -93,15 +93,15 @@ public class Engine {
/** used to test for cache hit */
private final String algorithm;
/** used to test for cache validity */
- private final int refreshNumber;
+ private final int cacheVersion;
/** cached result */
private final Provider.Service service;
private ServiceCacheEntry(String algorithm,
- int refreshNumber,
+ int cacheVersion,
Provider.Service service) {
this.algorithm = algorithm;
- this.refreshNumber = refreshNumber;
+ this.cacheVersion = cacheVersion;
this.service = service;
}
}
@@ -134,12 +134,12 @@ public class Engine {
if (algorithm == null) {
throw new NoSuchAlgorithmException("Null algorithm name");
}
- Services.refresh();
+ int newCacheVersion = Services.getCacheVersion();
Provider.Service service;
ServiceCacheEntry cacheEntry = this.serviceCache;
if (cacheEntry != null
&& cacheEntry.algorithm.equalsIgnoreCase(algorithm)
- && Services.refreshNumber == cacheEntry.refreshNumber) {
+ && newCacheVersion == cacheEntry.cacheVersion) {
service = cacheEntry.service;
} else {
if (Services.isEmpty()) {
@@ -150,7 +150,7 @@ public class Engine {
if (service == null) {
throw notFound(serviceName, algorithm);
}
- this.serviceCache = new ServiceCacheEntry(algorithm, Services.refreshNumber, service);
+ this.serviceCache = new ServiceCacheEntry(algorithm, newCacheVersion, service);
}
return new SpiAndProvider(service.newInstance(param), service.getProvider());
}
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
index d97e0f4..4fe0d44 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
@@ -15,11 +15,6 @@
* limitations under the License.
*/
-/**
-* @author Boris V. Kuznetsov
-* @version $Revision$
-*/
-
package org.apache.harmony.security.fortress;
import java.security.Provider;
@@ -34,87 +29,83 @@ import java.util.Map;
/**
* This class contains information about all registered providers and preferred
* implementations for all "serviceName.algName".
- *
*/
-
public class Services {
- // The HashMap that contains information about preferred implementations for
- // all serviceName.algName in the registered providers.
- // Set the initial size to 600 so we don't grow to 1024 by default because
- // initialization adds a few entries more than the growth threshold.
+ /**
+ * The HashMap that contains information about preferred implementations for
+ * all serviceName.algName in the registered providers.
+ * Set the initial size to 600 so we don't grow to 1024 by default because
+ * initialization adds a few entries more than the growth threshold.
+ */
private static final Map<String, Provider.Service> services
= new HashMap<String, Provider.Service>(600);
- // Save default SecureRandom service as well.
- // Avoids similar provider/services iteration in SecureRandom constructor
- private static Provider.Service secureRandom;
- // Need refresh flag
- private static boolean needRefresh; // = false;
+ /**
+ * Save default SecureRandom service as well.
+ * Avoids similar provider/services iteration in SecureRandom constructor.
+ */
+ private static Provider.Service cachedSecureRandomService;
+
+ /**
+ * Need refresh flag.
+ */
+ private static boolean needRefresh;
/**
- * Refresh number
+ * The cacheVersion is changed on every update of service
+ * information. It is used by external callers to validate their
+ * own caches of Service information.
*/
- static int refreshNumber = 1;
+ private static int cacheVersion = 1;
- // Registered providers
+ /**
+ * Registered providers.
+ */
private static final List<Provider> providers = new ArrayList<Provider>(20);
- // Hash for quick provider access by name
+ /**
+ * Hash for quick provider access by name.
+ */
private static final Map<String, Provider> providersNames = new HashMap<String, Provider>(20);
static {
- loadProviders();
- }
-
- // Load statically registered providers and init Services Info
- private static void loadProviders() {
String providerClassName = null;
int i = 1;
ClassLoader cl = ClassLoader.getSystemClassLoader();
- Provider p;
- while ((providerClassName = Security.getProperty("security.provider."
- + i++)) != null) {
+ while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
try {
- p = (Provider) Class
- .forName(providerClassName.trim(), true, cl)
- .newInstance();
+ Class providerClass = Class.forName(providerClassName.trim(), true, cl);
+ Provider p = (Provider) providerClass.newInstance();
providers.add(p);
providersNames.put(p.getName(), p);
initServiceInfo(p);
- } catch (ClassNotFoundException e) { // ignore Exceptions
- } catch (IllegalAccessException e) {
- } catch (InstantiationException e) {
+ } catch (ClassNotFoundException ignored) {
+ } catch (IllegalAccessException ignored) {
+ } catch (InstantiationException ignored) {
}
}
Engine.door.renumProviders();
}
/**
- * Returns registered providers
- *
- * @return
+ * Returns a copy of the registered providers as an array.
*/
- public static Provider[] getProviders() {
+ public static synchronized Provider[] getProviders() {
return providers.toArray(new Provider[providers.size()]);
}
/**
- * Returns registered providers as List
- *
- * @return
+ * Returns a copy of the registered providers as a list.
*/
- public static List<Provider> getProvidersList() {
+ public static synchronized List<Provider> getProvidersList() {
return new ArrayList<Provider>(providers);
}
/**
- * Returns the provider with the specified name
- *
- * @param name
- * @return
+ * Returns the provider with the specified name.
*/
- public static Provider getProvider(String name) {
+ public static synchronized Provider getProvider(String name) {
if (name == null) {
return null;
}
@@ -122,13 +113,9 @@ public class Services {
}
/**
- * Inserts a provider at a specified position
- *
- * @param provider
- * @param position
- * @return
+ * Inserts a provider at a specified 1-based position.
*/
- public static int insertProviderAt(Provider provider, int position) {
+ public static synchronized int insertProviderAt(Provider provider, int position) {
int size = providers.size();
if ((position < 1) || (position > size)) {
position = size + 1;
@@ -140,98 +127,91 @@ public class Services {
}
/**
- * Removes the provider
- *
- * @param providerNumber
+ * Removes the provider at the specified 1-based position.
*/
- public static void removeProvider(int providerNumber) {
+ public static synchronized void removeProvider(int providerNumber) {
Provider p = providers.remove(providerNumber - 1);
providersNames.remove(p.getName());
setNeedRefresh();
}
/**
- *
* Adds information about provider services into HashMap.
- *
- * @param p
*/
- public static void initServiceInfo(Provider p) {
- for (Provider.Service serv : p.getServices()) {
- String type = serv.getType();
- if (secureRandom == null && type.equals("SecureRandom")) {
- secureRandom = serv;
+ public static synchronized void initServiceInfo(Provider p) {
+ for (Provider.Service service : p.getServices()) {
+ String type = service.getType();
+ if (cachedSecureRandomService == null && type.equals("SecureRandom")) {
+ cachedSecureRandomService = service;
}
- String key = type + "." + serv.getAlgorithm().toUpperCase(Locale.US);
+ String key = type + "." + service.getAlgorithm().toUpperCase(Locale.US);
if (!services.containsKey(key)) {
- services.put(key, serv);
+ services.put(key, service);
}
- for (String alias : Engine.door.getAliases(serv)) {
+ for (String alias : Engine.door.getAliases(service)) {
key = type + "." + alias.toUpperCase(Locale.US);
if (!services.containsKey(key)) {
- services.put(key, serv);
+ services.put(key, service);
}
}
}
}
/**
- *
- * Updates services hashtable for all registered providers
- *
- */
- public static void updateServiceInfo() {
- services.clear();
- secureRandom = null;
- for (Provider p : providers) {
- initServiceInfo(p);
- }
- needRefresh = false;
- }
-
- /**
- * Returns true if services contain any provider information
- * @return
+ * Returns true if services contain any provider information.
*/
- public static boolean isEmpty() {
+ public static synchronized boolean isEmpty() {
return services.isEmpty();
}
/**
- * Returns service description.
- * Call refresh() before.
+ * Looks up the requested service by type and algorithm. The
+ * service key should be provided in the same format used when
+ * registering a service with a provider, for example,
+ * "KeyFactory.RSA".
*
- * @param key in the format TYPE.ALGORITHM
- * @return
+ * Callers can cache the returned service information but such
+ * caches should be validated against the result of
+ * Service.getCacheVersion() before use.
*/
- public static Provider.Service getService(String key) {
+ public static synchronized Provider.Service getService(String key) {
return services.get(key);
}
/**
* Returns the default SecureRandom service description.
- * Call refresh() before.
*/
- public static Provider.Service getSecureRandomService() {
- return secureRandom;
+ public static synchronized Provider.Service getSecureRandomService() {
+ getCacheVersion(); // used for side effect of updating cache if needed
+ return cachedSecureRandomService;
}
/**
- * Set flag needRefresh
- *
+ * In addition to being used here when the list of providers
+ * changes, this method is also used by the Provider
+ * implementation to indicate that a provides list of services has
+ * changed.
*/
- public static void setNeedRefresh() {
+ public static synchronized void setNeedRefresh() {
needRefresh = true;
}
/**
- * Refresh services info
- *
+ * Returns the current cache version. This has the possible side
+ * effect of updating the cache if needed.
*/
- public static void refresh() {
+ public static synchronized int getCacheVersion() {
if (needRefresh) {
- refreshNumber++;
- updateServiceInfo();
+ cacheVersion++;
+ synchronized (services) {
+ services.clear();
+ }
+ cachedSecureRandomService = null;
+ for (Provider p : providers) {
+ initServiceInfo(p);
+ }
+ needRefresh = false;
}
+ return cacheVersion;
}
}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
index 134d8f9..68ec38a 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
@@ -237,7 +237,7 @@ public class X509CRLImpl extends X509CRL {
*/
public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
if (certificate == null) {
- throw new NullPointerException();
+ throw new NullPointerException("certificate == null");
}
if (!entriesRetrieved) {
retrieveEntries();
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
index d2a9c6d..2958e00 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
@@ -57,7 +57,7 @@ public class SHA1withDSA_SignatureImpl extends Signature {
protected Object engineGetParameter(String param)
throws InvalidParameterException {
if (param == null) {
- throw new NullPointerException();
+ throw new NullPointerException("param == null");
}
return null;
}
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
index be43ba7..4985aff 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
@@ -70,6 +70,34 @@ public final class AuthorityKeyIdentifier extends ExtensionValue {
return aki;
}
+ /**
+ * The key identifier for the authority.
+ *
+ * @return key identifier or {@code null}
+ */
+ public byte[] getKeyIdentifier() {
+ return keyIdentifier;
+ }
+
+ /**
+ * The GeneralNames for this authority key identifier.
+ *
+ * @return names for the authority certificate issuer or {@code null}
+ */
+ public GeneralNames getAuthorityCertIssuer() {
+ return authorityCertIssuer;
+ }
+
+ /**
+ * The serial number of the certificate identified by this authority key
+ * identifier.
+ *
+ * @return authority's certificate serial number or {@code null}
+ */
+ public BigInteger getAuthorityCertSerialNumber() {
+ return authorityCertSerialNumber;
+ }
+
@Override public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1.encode(this);
@@ -110,10 +138,10 @@ public final class AuthorityKeyIdentifier extends ExtensionValue {
@Override protected Object getDecodedObject(BerInputStream in) throws IOException {
Object[] values = (Object[]) in.content;
- byte[] enc = (byte[]) values[2];
+ byte[] bytes = (byte[]) values[2];
BigInteger authorityCertSerialNumber = null;
- if (enc != null) {
- authorityCertSerialNumber = new BigInteger(enc);
+ if (bytes != null) {
+ authorityCertSerialNumber = new BigInteger(bytes);
}
return new AuthorityKeyIdentifier((byte[]) values[0],
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
index 7415002..1db9598 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
@@ -58,6 +58,13 @@ public final class SubjectKeyIdentifier extends ExtensionValue {
return res;
}
+ /**
+ * The key identifier for this subject.
+ */
+ public byte[] getKeyIdentifier() {
+ return keyIdentifier;
+ }
+
@Override public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1OctetString.getInstance().encode(keyIdentifier);
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
index b92fc50..5ec632c 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
@@ -76,10 +76,10 @@ abstract class ExpatAttributes implements Attributes {
public int getIndex(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -90,7 +90,7 @@ abstract class ExpatAttributes implements Attributes {
public int getIndex(String qName) {
if (qName == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("qName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -101,10 +101,10 @@ abstract class ExpatAttributes implements Attributes {
public String getType(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
return getIndex(uri, localName) == -1 ? null : CDATA;
}
@@ -115,10 +115,10 @@ abstract class ExpatAttributes implements Attributes {
public String getValue(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -129,7 +129,7 @@ abstract class ExpatAttributes implements Attributes {
public String getValue(String qName) {
if (qName == null) {
- throw new NullPointerException("qName");
+ throw new NullPointerException("qName == null");
}
int pointer = getPointer();
if (pointer == 0) {
diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
index 1f1293b..af4002f 100644
--- a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
@@ -697,7 +697,7 @@ public abstract class NodeImpl implements Node {
public final Object setUserData(String key, Object data, UserDataHandler handler) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
Map<String, UserData> map = document.getUserDataMap(this);
UserData previous = data == null
@@ -708,7 +708,7 @@ public abstract class NodeImpl implements Node {
public final Object getUserData(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
Map<String, UserData> map = document.getUserDataMapForRead(this);
UserData userData = map.get(key);
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
index 8efaa30..debbb20 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
@@ -42,7 +42,7 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
@Override
public boolean getFeature(String name) throws ParserConfigurationException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (NAMESPACES.equals(name)) {
@@ -90,7 +90,7 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
public void setFeature(String name, boolean value)
throws ParserConfigurationException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (NAMESPACES.equals(name)) {
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
index 08657b9..e2e3778 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
@@ -41,7 +41,7 @@ public class SAXParserFactoryImpl extends SAXParserFactory {
@Override
public boolean getFeature(String name) throws SAXNotRecognizedException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (!name.startsWith("http://xml.org/sax/features/")) {
@@ -86,7 +86,7 @@ public class SAXParserFactoryImpl extends SAXParserFactory {
@Override
public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (!name.startsWith("http://xml.org/sax/features/")) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java
new file mode 100644
index 0000000..4cdd8d2
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.DefaultHostnameVerifier;
+import libcore.io.IoUtils;
+import libcore.util.BasicLruCache;
+
+/**
+ * This class provides a simple interface for cert pinning.
+ */
+public class CertPinManager {
+
+ private long lastModified;
+
+ private final Map<String, PinListEntry> entries = new HashMap<String, PinListEntry>();
+ private final BasicLruCache<String, String> hostnameCache = new BasicLruCache<String, String>(10);
+ private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+
+ private boolean initialized = false;
+ private static final boolean DEBUG = false;
+
+ private final File pinFile;
+ private final TrustedCertificateStore certStore;
+
+ public CertPinManager(TrustedCertificateStore store) throws PinManagerException {
+ pinFile = new File("/data/misc/keychain/pins");
+ certStore = store;
+ rebuild();
+ }
+
+ /** Test only */
+ public CertPinManager(String path, TrustedCertificateStore store) throws PinManagerException {
+ if (path == null) {
+ throw new NullPointerException("path == null");
+ }
+ pinFile = new File(path);
+ certStore = store;
+ rebuild();
+ }
+
+ /**
+ * This is the public interface for cert pinning.
+ *
+ * Given a hostname and a certificate chain this verifies that the chain includes
+ * certs from the pinned list provided.
+ *
+ * If the chain doesn't include those certs and is in enforcing mode, then this method
+ * returns true and the certificate check should fail.
+ */
+ public boolean chainIsNotPinned(String hostname, List<X509Certificate> chain)
+ throws PinManagerException {
+ // lookup the entry
+ PinListEntry entry = lookup(hostname);
+
+ // return its result or false if there's no pin
+ if (entry != null) {
+ return entry.chainIsNotPinned(chain);
+ }
+ return false;
+ }
+
+ private synchronized void rebuild() throws PinManagerException {
+ // reread the pin file
+ String pinFileContents = readPinFile();
+
+ if (pinFileContents != null) {
+ // rebuild the pinned certs
+ for (String entry : getPinFileEntries(pinFileContents)) {
+ try {
+ PinListEntry pin = new PinListEntry(entry, certStore);
+ entries.put(pin.getCommonName(), pin);
+ } catch (PinEntryException e) {
+ log("Pinlist contains a malformed pin: " + entry, e);
+ }
+ }
+
+ // clear the cache
+ hostnameCache.evictAll();
+
+ // set the last modified time
+ lastModified = pinFile.lastModified();
+
+ // we've been fully initialized and are ready to go
+ initialized = true;
+ }
+ }
+
+ private String readPinFile() throws PinManagerException {
+ try {
+ return IoUtils.readFileAsString(pinFile.getPath());
+ } catch (FileNotFoundException e) {
+ // there's no pin list, all certs are unpinned
+ return null;
+ } catch (IOException e) {
+ // this is unexpected, fail
+ throw new PinManagerException("Unexpected error reading pin list; failing.", e);
+ }
+ }
+
+ private static String[] getPinFileEntries(String pinFileContents) {
+ return pinFileContents.split("\n");
+ }
+
+ private synchronized PinListEntry lookup(String hostname) throws PinManagerException {
+
+ // if we don't have any data, don't bother
+ if (!initialized) {
+ return null;
+ }
+
+ // check to see if our cache is valid
+ if (cacheIsNotValid()) {
+ rebuild();
+ }
+
+ // if so, check the hostname cache
+ String cn = hostnameCache.get(hostname);
+ if (cn != null) {
+ // if we hit, return the corresponding entry
+ return entries.get(cn);
+ }
+
+ // otherwise, get the matching cn
+ cn = getMatchingCN(hostname);
+ if (cn != null) {
+ hostnameCache.put(hostname, cn);
+ // we have a matching CN, return that entry
+ return entries.get(cn);
+ }
+
+ // if we got here, we don't have a matching CN for this hostname
+ return null;
+ }
+
+ private boolean cacheIsNotValid() {
+ return pinFile.lastModified() != lastModified;
+ }
+
+ private String getMatchingCN(String hostname) {
+ String bestMatch = "";
+ for (String cn : entries.keySet()) {
+ // skip shorter CNs since they can't be better matches
+ if (cn.length() < bestMatch.length()) {
+ continue;
+ }
+ // now verify that the CN matches at all
+ if (verifier.verifyHostName(hostname, cn)) {
+ bestMatch = cn;
+ }
+ }
+ return bestMatch;
+ }
+
+ private static void log(String s, Exception e) {
+ if (DEBUG) {
+ System.out.println("PINFILE: " + s);
+ if (e != null) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
index 4b29363..c855c0c 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
@@ -18,14 +18,12 @@
package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
-import java.security.AccessController;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.PrivilegedExceptionAction;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -39,6 +37,7 @@ import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
/**
@@ -90,7 +89,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
if (engineOwner != null) {
session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
} else {
- session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort());
+ session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort());
}
session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
recordProtocol.setVersion(session.protocol.version);
@@ -111,7 +110,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
if (engineOwner != null) {
session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
} else {
- session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort());
+ session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort());
}
session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
recordProtocol.setVersion(session.protocol.version);
@@ -500,7 +499,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
// send certificate verify for all certificates except those containing
// fixed DH parameters
- if (clientCert != null && !clientKeyExchange.isEmpty()) {
+ if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) {
// Certificate verify
String authType = clientKey.getAlgorithm();
DigitalSignature ds = new DigitalSignature(authType);
@@ -529,8 +528,21 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
if (authType == null) {
return;
}
+ String hostname = null;
+ if (engineOwner != null) {
+ hostname = engineOwner.getPeerHost();
+ } else {
+ // we don't want to do an inet address lookup here in case we're talking to a proxy
+ hostname = socketOwner.getWrappedHostName();
+ }
try {
- parameters.getTrustManager().checkServerTrusted(serverCert.certs, authType);
+ X509TrustManager x509tm = parameters.getTrustManager();
+ if (x509tm instanceof TrustManagerImpl) {
+ TrustManagerImpl tm = (TrustManagerImpl) x509tm;
+ tm.checkServerTrusted(serverCert.certs, authType, hostname);
+ } else {
+ x509tm.checkServerTrusted(serverCert.certs, authType);
+ }
} catch (CertificateException e) {
fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e);
return;
@@ -561,8 +573,8 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
host = engineOwner.getPeerHost();
port = engineOwner.getPeerPort();
} else {
- host = socketOwner.getInetAddress().getHostName();
- port = socketOwner.getPort();
+ host = socketOwner.getPeerHostName();
+ port = socketOwner.getPeerPort();
}
if (host == null || port == -1) {
return null; // starts new session
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
index 6619d1d..0f1fe24 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
@@ -121,7 +121,7 @@ public class FileClientSessionCache {
*/
private static String fileName(String host, int port) {
if (host == null) {
- throw new NullPointerException("host");
+ throw new NullPointerException("host == null");
}
return host + "." + port;
}
@@ -182,7 +182,7 @@ public class FileClientSessionCache {
byte[] sessionData) {
String host = session.getPeerHost();
if (sessionData == null) {
- throw new NullPointerException("sessionData");
+ throw new NullPointerException("sessionData == null");
}
String name = fileName(host, session.getPeerPort());
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
index a2688e2..2fbbef7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -19,8 +19,6 @@ package org.apache.harmony.xnet.provider.jsse;
import java.io.PrintStream;
import java.util.Locale;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import libcore.util.EmptyArray;
/**
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 759fc85..65373ff 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -22,6 +22,7 @@ import java.net.SocketTimeoutException;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
@@ -31,6 +32,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import javax.crypto.BadPaddingException;
import javax.net.ssl.SSLException;
import javax.security.auth.x500.X500Principal;
import libcore.io.Memory;
@@ -52,6 +54,8 @@ public final class NativeCrypto {
public static native int ENGINE_by_id(String id);
+ public static native int ENGINE_add(int e);
+
public static native int ENGINE_init(int e);
public static native int ENGINE_finish(int e);
@@ -74,6 +78,8 @@ public final class NativeCrypto {
public static native void EVP_PKEY_free(int pkey);
+ public static native int EVP_PKEY_cmp(int pkey1, int pkey2);
+
public static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int pkey);
public static native int d2i_PKCS8_PRIV_KEY_INFO(byte[] data);
@@ -84,6 +90,20 @@ public final class NativeCrypto {
public static native int RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
+ public static native int RSA_size(int pkey);
+
+ public static native int RSA_private_encrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding);
+
+ public static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding) throws BadPaddingException, SignatureException;
+
+ public static native int RSA_public_encrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding);
+
+ public static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding) throws BadPaddingException, SignatureException;
+
/**
* @return array of {n, e}
*/
@@ -172,6 +192,8 @@ public final class NativeCrypto {
public static native int RAND_load_file(String filename, long max_bytes);
+ public static native void RAND_bytes(byte[] output);
+
// --- X509_NAME -----------------------------------------------------------
public static int X509_NAME_hash(X500Principal principal) {
@@ -333,13 +355,16 @@ public final class NativeCrypto {
public static final int EVP_PKEY_DH = 28; // NID_dhKeyAgreement
public static final int EVP_PKEY_EC = 408; // NID_X9_62_id_ecPublicKey
+ // RSA padding modes from rsa.h
+ public static final int RSA_PKCS1_PADDING = 1;
+ public static final int RSA_NO_PADDING = 3;
+
// SSL mode from ssl.h
public static final long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
// SSL options from ssl.h
public static final long SSL_OP_NO_TICKET = 0x00004000L;
public static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L;
- public static final long SSL_OP_NO_COMPRESSION = 0x00020000L;
public static final long SSL_OP_NO_SSLv3 = 0x02000000L;
public static final long SSL_OP_NO_TLSv1 = 0x04000000L;
public static final long SSL_OP_NO_TLSv1_1 = 0x10000000L;
@@ -544,66 +569,6 @@ public final class NativeCrypto {
return cipherSuites;
}
- public static final String SUPPORTED_COMPRESSION_METHOD_ZLIB = "ZLIB";
- public static final String SUPPORTED_COMPRESSION_METHOD_NULL = "NULL";
-
- private static final String[] SUPPORTED_COMPRESSION_METHODS
- = { SUPPORTED_COMPRESSION_METHOD_ZLIB, SUPPORTED_COMPRESSION_METHOD_NULL };
-
- public static String[] getSupportedCompressionMethods() {
- return SUPPORTED_COMPRESSION_METHODS.clone();
- }
-
- public static final String[] getDefaultCompressionMethods() {
- return new String[] { SUPPORTED_COMPRESSION_METHOD_NULL };
- }
-
- public static String[] checkEnabledCompressionMethods(String[] methods) {
- if (methods == null) {
- throw new IllegalArgumentException("methods == null");
- }
- if (methods.length < 1
- && !methods[methods.length-1].equals(SUPPORTED_COMPRESSION_METHOD_NULL)) {
- throw new IllegalArgumentException("last method must be NULL");
- }
- for (int i = 0; i < methods.length; i++) {
- String method = methods[i];
- if (method == null) {
- throw new IllegalArgumentException("methods[" + i + "] == null");
- }
- if (!method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB)
- && !method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) {
- throw new IllegalArgumentException("method " + method
- + " is not supported");
- }
- }
- return methods;
- }
-
- public static void setEnabledCompressionMethods(int ssl, String[] methods) {
- checkEnabledCompressionMethods(methods);
- // openssl uses negative logic letting you disable compression.
- // so first, assume we need to set all (disable all) and clear none (enable none).
- // in the loop, selectively move bits from set to clear (from disable to enable)
- long optionsToSet = (SSL_OP_NO_COMPRESSION);
- long optionsToClear = 0;
- for (int i = 0; i < methods.length; i++) {
- String method = methods[i];
- if (method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) {
- // nothing to do to support NULL
- } else if (method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB)) {
- optionsToSet &= ~SSL_OP_NO_COMPRESSION;
- optionsToClear |= SSL_OP_NO_COMPRESSION;
- } else {
- // error checked by checkEnabledCompressionMethods
- throw new IllegalStateException();
- }
- }
-
- SSL_set_options(ssl, optionsToSet);
- SSL_clear_options(ssl, optionsToClear);
- }
-
/*
* See the OpenSSL ssl.h header file for more information.
*/
@@ -680,7 +645,7 @@ public final class NativeCrypto {
public static native int SSL_read(int sslNativePointer,
FileDescriptor fd,
SSLHandshakeCallbacks shc,
- byte[] b, int off, int len, int timeoutMillis)
+ byte[] b, int off, int len, int readTimeoutMillis)
throws IOException;
/**
@@ -689,7 +654,7 @@ public final class NativeCrypto {
public static native void SSL_write(int sslNativePointer,
FileDescriptor fd,
SSLHandshakeCallbacks shc,
- byte[] b, int off, int len)
+ byte[] b, int off, int len, int writeTimeoutMillis)
throws IOException;
public static native void SSL_interrupt(int sslNativePointer);
@@ -707,9 +672,6 @@ public final class NativeCrypto {
public static native String SSL_SESSION_cipher(int sslSessionNativePointer);
- public static native String SSL_SESSION_compress_meth(int sslCtxNativePointer,
- int sslSessionNativePointer);
-
public static native void SSL_SESSION_free(int sslSessionNativePointer);
public static native byte[] i2d_SSL_SESSION(int sslSessionNativePointer);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java
new file mode 100644
index 0000000..ddf2e0d
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+import libcore.util.EmptyArray;
+
+public abstract class OpenSSLCipherRSA extends CipherSpi {
+ /**
+ * The current OpenSSL key we're operating on.
+ */
+ private OpenSSLKey key;
+
+ /**
+ * Current key type: private or public.
+ */
+ private boolean usingPrivateKey;
+
+ /**
+ * Current cipher mode: encrypting or decrypting.
+ */
+ private boolean encrypting;
+
+ /**
+ * Buffer for operations
+ */
+ private byte[] buffer;
+
+ /**
+ * Current offset in the buffer.
+ */
+ private int bufferOffset;
+
+ /**
+ * Flag that indicates an exception should be thrown when the input is too
+ * large during doFinal.
+ */
+ private boolean inputTooLarge;
+
+ /**
+ * Current padding mode
+ */
+ private int padding = NativeCrypto.RSA_PKCS1_PADDING;
+
+ protected OpenSSLCipherRSA(int padding) {
+ this.padding = padding;
+ }
+
+ @Override
+ protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+ final String modeUpper = mode.toUpperCase();
+ if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) {
+ return;
+ }
+
+ throw new NoSuchAlgorithmException("mode not supported: " + mode);
+ }
+
+ @Override
+ protected void engineSetPadding(String padding) throws NoSuchPaddingException {
+ final String paddingUpper = padding.toUpperCase();
+ if ("PKCS1PADDING".equals(paddingUpper)) {
+ this.padding = NativeCrypto.RSA_PKCS1_PADDING;
+ return;
+ }
+ if ("NOPADDING".equals(paddingUpper)) {
+ this.padding = NativeCrypto.RSA_NO_PADDING;
+ return;
+ }
+
+ throw new NoSuchPaddingException("padding not supported: " + padding);
+ }
+
+ @Override
+ protected int engineGetBlockSize() {
+ if (encrypting) {
+ return paddedBlockSizeBytes();
+ }
+ return keySizeBytes();
+ }
+
+ @Override
+ protected int engineGetOutputSize(int inputLen) {
+ if (encrypting) {
+ return keySizeBytes();
+ }
+ return paddedBlockSizeBytes();
+ }
+
+ private int paddedBlockSizeBytes() {
+ int paddedBlockSizeBytes = keySizeBytes();
+ if (padding == NativeCrypto.RSA_PKCS1_PADDING) {
+ paddedBlockSizeBytes--; // for 0 prefix
+ paddedBlockSizeBytes -= 10; // PKCS1 padding header length
+ }
+ return paddedBlockSizeBytes;
+ }
+
+ private int keySizeBytes() {
+ if (key == null) {
+ throw new IllegalStateException("cipher is not initialized");
+ }
+ return NativeCrypto.RSA_size(this.key.getPkeyContext());
+ }
+
+ @Override
+ protected byte[] engineGetIV() {
+ return null;
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ private void engineInitInternal(int opmode, Key key) throws InvalidKeyException {
+ if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+ encrypting = true;
+ } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+ encrypting = false;
+ } else {
+ throw new InvalidParameterException("Unsupported opmode " + opmode);
+ }
+
+ if (key instanceof OpenSSLRSAPrivateKey) {
+ OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key;
+ usingPrivateKey = true;
+ this.key = rsaPrivateKey.getOpenSSLKey();
+ } else if (key instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key;
+ usingPrivateKey = true;
+ this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+ } else if (key instanceof RSAPrivateKey) {
+ RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key;
+ usingPrivateKey = true;
+ this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+ } else if (key instanceof OpenSSLRSAPublicKey) {
+ OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key;
+ usingPrivateKey = false;
+ this.key = rsaPublicKey.getOpenSSLKey();
+ } else if (key instanceof RSAPublicKey) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) key;
+ usingPrivateKey = false;
+ this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+ } else {
+ throw new InvalidKeyException("Need RSA private or public key");
+ }
+
+ buffer = new byte[NativeCrypto.RSA_size(this.key.getPkeyContext())];
+ inputTooLarge = false;
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException("unknown param type: "
+ + params.getClass().getName());
+ }
+
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException("unknown param type: "
+ + params.getClass().getName());
+ }
+
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ if (bufferOffset + inputLen > buffer.length) {
+ inputTooLarge = true;
+ return EmptyArray.BYTE;
+ }
+
+ System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen);
+ bufferOffset += inputLen;
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException {
+ engineUpdate(input, inputOffset, inputLen);
+ return 0;
+ }
+
+ @Override
+ protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException {
+ if (input != null) {
+ engineUpdate(input, inputOffset, inputLen);
+ }
+
+ if (inputTooLarge) {
+ throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes");
+ }
+
+ final byte[] tmpBuf;
+ if (bufferOffset != buffer.length) {
+ if (padding == NativeCrypto.RSA_NO_PADDING) {
+ tmpBuf = new byte[buffer.length];
+ System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset);
+ } else {
+ tmpBuf = Arrays.copyOf(buffer, bufferOffset);
+ }
+ } else {
+ tmpBuf = buffer;
+ }
+
+ byte[] output = new byte[buffer.length];
+ int resultSize;
+ if (encrypting) {
+ if (usingPrivateKey) {
+ resultSize = NativeCrypto.RSA_private_encrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ } else {
+ resultSize = NativeCrypto.RSA_public_encrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ }
+ } else {
+ try {
+ if (usingPrivateKey) {
+ resultSize = NativeCrypto.RSA_private_decrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ } else {
+ resultSize = NativeCrypto.RSA_public_decrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ }
+ } catch (SignatureException e) {
+ IllegalBlockSizeException newE = new IllegalBlockSizeException();
+ newE.initCause(e);
+ throw newE;
+ }
+ }
+ if (!encrypting && resultSize != output.length) {
+ output = Arrays.copyOf(output, resultSize);
+ }
+
+ bufferOffset = 0;
+ return output;
+ }
+
+ @Override
+ protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ byte[] b = engineDoFinal(input, inputOffset, inputLen);
+
+ final int lastOffset = outputOffset + b.length;
+ if (lastOffset > output.length) {
+ throw new ShortBufferException("output buffer is too small " + output.length + " < "
+ + lastOffset);
+ }
+
+ System.arraycopy(b, 0, output, outputOffset, b.length);
+ return b.length;
+ }
+
+ @Override
+ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
+ try {
+ byte[] encoded = key.getEncoded();
+ return engineDoFinal(encoded, 0, encoded.length);
+ } catch (BadPaddingException e) {
+ IllegalBlockSizeException newE = new IllegalBlockSizeException();
+ newE.initCause(e);
+ throw newE;
+ }
+ }
+
+ @Override
+ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+ int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+ try {
+ byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+ if (wrappedKeyType == Cipher.PUBLIC_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.SECRET_KEY) {
+ return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+ } else {
+ throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
+ }
+ } catch (IllegalBlockSizeException e) {
+ throw new InvalidKeyException(e);
+ } catch (BadPaddingException e) {
+ throw new InvalidKeyException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ public static class PKCS1 extends OpenSSLCipherRSA {
+ public PKCS1() {
+ super(NativeCrypto.RSA_PKCS1_PADDING);
+ }
+ }
+
+ public static class Raw extends OpenSSLCipherRSA {
+ public Raw() {
+ super(NativeCrypto.RSA_NO_PADDING);
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
index 7cd16f7..761b08e 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
@@ -120,6 +120,10 @@ public class OpenSSLDSAPrivateKey implements DSAPrivateKey {
return key.getPkeyContext();
}
+ public String getPkeyAlias() {
+ return key.getAlias();
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
index e91e6d8..d01dc62 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
@@ -24,6 +24,8 @@ public class OpenSSLEngine {
NativeCrypto.ENGINE_load_dynamic();
}
+ private static final Object mLoadingLock = new Object();
+
/** The ENGINE's native handle. */
private final int ctx;
@@ -32,10 +34,14 @@ public class OpenSSLEngine {
throw new NullPointerException("engine == null");
}
- final int engineCtx = NativeCrypto.ENGINE_by_id(engine);
+ final int engineCtx;
+ synchronized (mLoadingLock) {
+ engineCtx = NativeCrypto.ENGINE_by_id(engine);
+ if (engineCtx == 0) {
+ throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
+ }
- if (engineCtx == 0) {
- throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
+ NativeCrypto.ENGINE_add(engineCtx);
}
return new OpenSSLEngine(engineCtx);
@@ -45,6 +51,7 @@ public class OpenSSLEngine {
ctx = engineCtx;
if (NativeCrypto.ENGINE_init(engineCtx) == 0) {
+ NativeCrypto.ENGINE_free(engineCtx);
throw new IllegalArgumentException("Could not initialize engine");
}
}
@@ -62,9 +69,9 @@ public class OpenSSLEngine {
final int keyType = NativeCrypto.EVP_PKEY_type(keyRef);
switch (keyType) {
case NativeCrypto.EVP_PKEY_RSA:
- return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this));
+ return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this, id));
case NativeCrypto.EVP_PKEY_DSA:
- return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this));
+ return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this, id));
default:
throw new InvalidKeyException("Unknown key type: " + keyType);
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
index 90eb0e2..b8b9f69 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
@@ -21,14 +21,18 @@ class OpenSSLKey {
private final OpenSSLEngine engine;
+ private final String alias;
+
OpenSSLKey(int ctx) {
this.ctx = ctx;
engine = null;
+ alias = null;
}
- OpenSSLKey(int ctx, OpenSSLEngine engine) {
+ OpenSSLKey(int ctx, OpenSSLEngine engine, String alias) {
this.ctx = ctx;
this.engine = engine;
+ this.alias = alias;
}
int getPkeyContext() {
@@ -43,6 +47,10 @@ class OpenSSLKey {
return engine != null;
}
+ String getAlias() {
+ return alias;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
index 97753cf..d4aa57f 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
@@ -73,33 +73,32 @@ public final class OpenSSLProvider extends Provider {
// put("KeyFactory.DSA", OpenSSLDSAKeyFactory.class.getName());
// Signatures
- put("Signature.MD5WithRSAEncryption", OpenSSLSignature.MD5RSA.class.getName());
- put("Alg.Alias.Signature.MD5WithRSA", "MD5WithRSAEncryption");
- put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1",
- "MD5WithRSAEncryption");
-
- put("Signature.SHA1WithRSAEncryption", OpenSSLSignature.SHA1RSA.class.getName());
- put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSAEncryption");
- put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSAEncryption");
-
- put("Signature.SHA256WithRSAEncryption", OpenSSLSignature.SHA256RSA.class.getName());
- put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSAEncryption");
-
- put("Signature.SHA384WithRSAEncryption", OpenSSLSignature.SHA384RSA.class.getName());
- put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSAEncryption");
-
- put("Signature.SHA512WithRSAEncryption", OpenSSLSignature.SHA512RSA.class.getName());
- put("Alg.Alias.Signature.SHA512WithRSA", "SHA512WithRSAEncryption");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSAEncryption");
+ put("Signature.MD5WithRSA", OpenSSLSignature.MD5RSA.class.getName());
+ put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA");
+ put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA");
+
+ put("Signature.SHA1WithRSA", OpenSSLSignature.SHA1RSA.class.getName());
+ put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSA");
+ put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA");
+ put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA");
+
+ put("Signature.SHA256WithRSA", OpenSSLSignature.SHA256RSA.class.getName());
+ put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA");
+
+ put("Signature.SHA384WithRSA", OpenSSLSignature.SHA384RSA.class.getName());
+ put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA");
+
+ put("Signature.SHA512WithRSA", OpenSSLSignature.SHA512RSA.class.getName());
+ put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA");
+ put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA");
put("Signature.SHA1withDSA", OpenSSLSignature.SHA1DSA.class.getName());
put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA");
@@ -108,5 +107,22 @@ public final class OpenSSLProvider extends Provider {
put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA");
put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA");
+
+ put("Signature.NONEwithRSA", OpenSSLSignatureRawRSA.class.getName());
+
+ // SecureRandom
+ /*
+ * We have to specify SHA1PRNG because various documentation mentions
+ * that algorithm by name instead of just recommending calling
+ * "new SecureRandom()"
+ */
+ put("SecureRandom.SHA1PRNG", OpenSSLRandom.class.getName());
+ put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+
+ // Cipher
+ put("Cipher.RSA/ECB/NoPadding", OpenSSLCipherRSA.Raw.class.getName());
+ put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+ put("Cipher.RSA/ECB/PKCS1Padding", OpenSSLCipherRSA.PKCS1.class.getName());
+ put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
}
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
index 8376515..4303e5a 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
@@ -191,8 +191,8 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA
return true;
}
- if (o instanceof OpenSSLRSAPrivateCrtKey) {
- OpenSSLRSAPrivateCrtKey other = (OpenSSLRSAPrivateCrtKey) o;
+ if (o instanceof OpenSSLRSAPrivateKey) {
+ OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
/*
* We can shortcut the true case, but it still may be equivalent but
@@ -201,19 +201,36 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA
if (getOpenSSLKey().equals(other.getOpenSSLKey())) {
return true;
}
+
+ return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1;
}
if (o instanceof RSAPrivateCrtKey) {
ensureReadParams();
RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;
- return getModulus().equals(other.getModulus())
- && publicExponent.equals(other.getPublicExponent())
- && getPrivateExponent().equals(other.getPrivateExponent())
- && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
- && primeExponentP.equals(other.getPrimeExponentP())
- && primeExponentQ.equals(other.getPrimeExponentQ())
- && crtCoefficient.equals(other.getCrtCoefficient());
+ if (getOpenSSLKey().isEngineBased()) {
+ return getModulus().equals(other.getModulus())
+ && publicExponent.equals(other.getPublicExponent());
+ } else {
+ return getModulus().equals(other.getModulus())
+ && publicExponent.equals(other.getPublicExponent())
+ && getPrivateExponent().equals(other.getPrivateExponent())
+ && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
+ && primeExponentP.equals(other.getPrimeExponentP())
+ && primeExponentQ.equals(other.getPrimeExponentQ())
+ && crtCoefficient.equals(other.getCrtCoefficient());
+ }
+ } else if (o instanceof RSAPrivateKey) {
+ ensureReadParams();
+ RSAPrivateKey other = (RSAPrivateKey) o;
+
+ if (getOpenSSLKey().isEngineBased()) {
+ return getModulus().equals(other.getModulus());
+ } else {
+ return getModulus().equals(other.getModulus())
+ && getPrivateExponent().equals(other.getPrivateExponent());
+ }
}
return false;
@@ -232,11 +249,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
- if (getOpenSSLKey().isEngineBased()) {
+ final boolean engineBased = getOpenSSLKey().isEngineBased();
+ if (engineBased) {
sb.append("key=");
sb.append(getOpenSSLKey());
sb.append('}');
- return sb.toString();
}
ensureReadParams();
@@ -250,9 +267,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA
sb.append(',');
}
- sb.append("privateExponent=");
- sb.append(getPrivateExponent().toString(16));
- sb.append(',');
+ if (!engineBased) {
+ sb.append("privateExponent=");
+ sb.append(getPrivateExponent().toString(16));
+ sb.append(',');
+ }
if (primeP != null) {
sb.append("primeP=");
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
index c9fa178..adb05a9 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
@@ -180,6 +180,10 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
return key.getPkeyContext();
}
+ public String getPkeyAlias() {
+ return key.getAlias();
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
@@ -196,6 +200,8 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
if (key.equals(other.getOpenSSLKey())) {
return true;
}
+
+ return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1;
}
if (o instanceof RSAPrivateKey) {
@@ -226,11 +232,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
- if (key.isEngineBased()) {
+ final boolean engineBased = key.isEngineBased();
+ if (engineBased) {
sb.append("key=");
sb.append(key);
sb.append('}');
- return sb.toString();
}
ensureReadParams();
@@ -238,9 +244,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
sb.append(modulus.toString(16));
sb.append(',');
- sb.append("privateExponent=");
- sb.append(privateExponent.toString(16));
- sb.append(',');
+ if (!engineBased) {
+ sb.append("privateExponent=");
+ sb.append(privateExponent.toString(16));
+ sb.append(',');
+ }
return sb.toString();
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java
new file mode 100644
index 0000000..fd011f0
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.Serializable;
+import java.security.SecureRandomSpi;
+
+public class OpenSSLRandom extends SecureRandomSpi implements Serializable {
+ private static final long serialVersionUID = 8506210602917522860L;
+
+ @Override
+ protected void engineSetSeed(byte[] seed) {
+ NativeCrypto.RAND_seed(seed);
+ }
+
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ NativeCrypto.RAND_bytes(bytes);
+ }
+
+ @Override
+ protected byte[] engineGenerateSeed(int numBytes) {
+ byte[] output = new byte[numBytes];
+ NativeCrypto.RAND_bytes(output);
+ return output;
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
index 841c31c..2f5fe59 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -32,7 +32,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
private final SSLParametersImpl sslParameters;
private String[] enabledProtocols = NativeCrypto.getSupportedProtocols();
private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
- private String[] enabledCompressionMethods = NativeCrypto.getDefaultCompressionMethods();
protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
this.sslParameters = sslParameters;
@@ -126,26 +125,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
}
- public String[] getSupportedCompressionMethods() {
- return NativeCrypto.getSupportedCompressionMethods();
- }
-
- public String[] getEnabledCompressionMethods() {
- return enabledCompressionMethods.clone();
- }
-
- /**
- * This method enables the compression methods listed by
- * getSupportedCompressionMethods().
- *
- * @param suites the names of all the compression methods to enable
- * @throws IllegalArgumentException when one or more of the ciphers in array
- * suites are not supported, or when the array is null.
- */
- public void setEnabledCompressionMethods(String[] methods) {
- enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods);
- }
-
@Override
public boolean getWantClientAuth() {
return sslParameters.getWantClientAuth();
@@ -185,8 +164,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters,
enabledProtocols.clone(),
- enabledCipherSuites.clone(),
- enabledCompressionMethods.clone());
+ enabledCipherSuites.clone());
implAccept(socket);
return socket;
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
index e194a38..003122f 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -49,7 +49,6 @@ public class OpenSSLSessionImpl implements SSLSession {
private int peerPort = -1;
private String cipherSuite;
private String protocol;
- private String compressionMethod;
private AbstractSessionContext sessionContext;
private byte[] id;
@@ -328,19 +327,6 @@ public class OpenSSLSessionImpl implements SSLSession {
}
/**
- * Returns the compression method name used in all connections
- * pertaining to this SSL session.
- */
- public String getCompressionMethod() {
- if (compressionMethod == null) {
- compressionMethod
- = NativeCrypto.SSL_SESSION_compress_meth(sessionContext.sslCtxNativePointer,
- sslSessionNativePointer);
- }
- return compressionMethod;
- }
-
- /**
* Returns the context to which the actual SSL session is bound. A SSL
* context consists of (1) a possible delegate, (2) a provider and (3) a
* protocol.
@@ -380,9 +366,7 @@ public class OpenSSLSessionImpl implements SSLSession {
/**
* Returns the object which is bound to the the input parameter name.
* This name is a sort of link to the data of the SSL session's application
- * layer, if any exists. The search for this link is monitored, as a matter
- * of security, by the full machinery of the <code>AccessController</code>
- * class.
+ * layer, if any exists.
*
* @param name the name of the binding to find.
* @return the value bound to that name, or null if the binding does not
@@ -398,9 +382,7 @@ public class OpenSSLSessionImpl implements SSLSession {
/**
* Returns an array with the names (sort of links) of all the data
- * objects of the application layer bound into the SSL session. The search
- * for this link is monitored, as a matter of security, by the full
- * machinery of the <code>AccessController</code> class.
+ * objects of the application layer bound into the SSL session.
*
* @return a non-null (possibly empty) array of names of the data objects
* bound to this SSL session.
@@ -413,9 +395,7 @@ public class OpenSSLSessionImpl implements SSLSession {
* A link (name) with the specified value object of the SSL session's
* application layer data is created or replaced. If the new (or existing)
* value object implements the <code>SSLSessionBindingListener</code>
- * interface, that object will be notified in due course. These links-to
- * -data bounds are monitored, as a matter of security, by the full
- * machinery of the <code>AccessController</code> class.
+ * interface, that object will be notified in due course.
*
* @param name the name of the link (no null are
* accepted!)
@@ -446,10 +426,6 @@ public class OpenSSLSessionImpl implements SSLSession {
* <p>If the value object implements the <code>SSLSessionBindingListener</code>
* interface, the object will receive a <code>valueUnbound</code> notification.
*
- * <p>These links-to -data bounds are
- * monitored, as a matter of security, by the full machinery of the
- * <code>AccessController</code> class.
- *
* @param name the name of the link (no null are
* accepted!)
* @throws <code>IllegalArgumentException</code> if the argument is null.
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java
new file mode 100644
index 0000000..289af30
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+
+/**
+ * Implements the JDK Signature interface needed for RAW RSA signature
+ * generation and verification using OpenSSL.
+ */
+public class OpenSSLSignatureRawRSA extends Signature {
+ /**
+ * The current OpenSSL key we're operating on.
+ */
+ private OpenSSLKey key;
+
+ /**
+ * Buffer to hold value to be signed or verified.
+ */
+ private byte[] inputBuffer;
+
+ /**
+ * Current offset in input buffer.
+ */
+ private int inputOffset;
+
+ /**
+ * Provides a flag to specify when the input is too long.
+ */
+ private boolean inputIsTooLong;
+
+ /**
+ * Creates a new OpenSSLSignature instance for the given algorithm name.
+ */
+ public OpenSSLSignatureRawRSA() throws NoSuchAlgorithmException {
+ super("NONEwithRSA");
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ final int oldOffset = inputOffset++;
+
+ if (inputOffset > inputBuffer.length) {
+ inputIsTooLong = true;
+ return;
+ }
+
+ inputBuffer[oldOffset] = input;
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ final int oldOffset = inputOffset;
+ inputOffset += len;
+
+ if (inputOffset > inputBuffer.length) {
+ inputIsTooLong = true;
+ return;
+ }
+
+ System.arraycopy(input, offset, inputBuffer, oldOffset, len);
+ }
+
+ @Override
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ return null;
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ if (privateKey instanceof OpenSSLRSAPrivateKey) {
+ OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
+ key = rsaPrivateKey.getOpenSSLKey();
+ } else if (privateKey instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
+ key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+ } else if (privateKey instanceof RSAPrivateKey) {
+ RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
+ key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+ } else {
+ throw new InvalidKeyException("Need DSA or RSA private key");
+ }
+
+ // Allocate buffer according to RSA modulus size.
+ int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
+ inputBuffer = new byte[maxSize];
+ inputOffset = 0;
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ if (publicKey instanceof OpenSSLRSAPublicKey) {
+ OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
+ key = rsaPublicKey.getOpenSSLKey();
+ } else if (publicKey instanceof RSAPublicKey) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+ key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+ } else {
+ throw new InvalidKeyException("Need DSA or RSA public key");
+ }
+
+ // Allocate buffer according to RSA modulus size.
+ int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
+ inputBuffer = new byte[maxSize];
+ inputOffset = 0;
+ }
+
+ @Override
+ protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("Need RSA private key");
+ }
+
+ if (inputIsTooLong) {
+ throw new SignatureException("input length " + inputOffset + " != "
+ + inputBuffer.length + " (modulus size)");
+ }
+
+ byte[] outputBuffer = new byte[inputBuffer.length];
+ try {
+ NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer,
+ key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
+ return outputBuffer;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ inputOffset = 0;
+ }
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("Need RSA public key");
+ }
+
+ if (inputIsTooLong) {
+ return false;
+ }
+
+ byte[] outputBuffer = new byte[inputBuffer.length];
+ try {
+ final int resultSize;
+ try {
+ resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes,
+ outputBuffer, key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
+ } catch (SignatureException e) {
+ throw e;
+ } catch (Exception e) {
+ return false;
+ }
+ /* Make this constant time by comparing every byte. */
+ boolean matches = (resultSize == inputOffset);
+ for (int i = 0; i < resultSize; i++) {
+ if (inputBuffer[i] != outputBuffer[i]) {
+ matches = false;
+ }
+ }
+ return matches;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ inputOffset = 0;
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 4c92952..4cc16e6 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -42,7 +42,11 @@ import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
+import static libcore.io.OsConstants.*;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
import libcore.io.Streams;
+import libcore.io.StructTimeval;
import org.apache.harmony.security.provider.cert.X509CertImpl;
/**
@@ -51,7 +55,6 @@ import org.apache.harmony.security.provider.cert.X509CertImpl;
* Extensions to SSLSocket include:
* <ul>
* <li>handshake timeout
- * <li>compression methods
* <li>session tickets
* <li>Server Name Indication
* </ul>
@@ -70,7 +73,6 @@ public class OpenSSLSocketImpl
private byte[] npnProtocols;
private String[] enabledProtocols;
private String[] enabledCipherSuites;
- private String[] enabledCompressionMethods;
private boolean useSessionTickets;
private String hostname;
private OpenSSLSessionImpl sslSession;
@@ -95,7 +97,8 @@ public class OpenSSLSocketImpl
* OpenSSLSocketImplWrapper overrides setSoTimeout and
* getSoTimeout to delegate to the wrapped socket.
*/
- private int timeoutMilliseconds = 0;
+ private int readTimeoutMilliseconds = 0;
+ private int writeTimeoutMilliseconds = 0;
private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite
private String wrappedHost;
@@ -108,10 +111,9 @@ public class OpenSSLSocketImpl
protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
String[] enabledProtocols,
- String[] enabledCipherSuites,
- String[] enabledCompressionMethods) throws IOException {
+ String[] enabledCipherSuites) throws IOException {
this.socket = this;
- init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods);
+ init(sslParameters, enabledProtocols, enabledCipherSuites);
}
protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
@@ -169,8 +171,7 @@ public class OpenSSLSocketImpl
private void init(SSLParametersImpl sslParameters) throws IOException {
init(sslParameters,
NativeCrypto.getDefaultProtocols(),
- NativeCrypto.getDefaultCipherSuites(),
- NativeCrypto.getDefaultCompressionMethods());
+ NativeCrypto.getDefaultCipherSuites());
}
/**
@@ -179,12 +180,10 @@ public class OpenSSLSocketImpl
*/
private void init(SSLParametersImpl sslParameters,
String[] enabledProtocols,
- String[] enabledCipherSuites,
- String[] enabledCompressionMethods) throws IOException {
+ String[] enabledCipherSuites) throws IOException {
this.sslParameters = sslParameters;
this.enabledProtocols = enabledProtocols;
this.enabledCipherSuites = enabledCipherSuites;
- this.enabledCompressionMethods = enabledCompressionMethods;
}
/**
@@ -225,20 +224,6 @@ public class OpenSSLSocketImpl
return null;
}
- String compressionMethod = session.getCompressionMethod();
- if (!compressionMethod.equals(NativeCrypto.SUPPORTED_COMPRESSION_METHOD_NULL)) {
- boolean compressionMethodFound = false;
- for (String enabledCompressionMethod : enabledCompressionMethods) {
- if (compressionMethod.equals(enabledCompressionMethod)) {
- compressionMethodFound = true;
- break;
- }
- }
- if (!compressionMethodFound) {
- return null;
- }
- }
-
return session;
}
@@ -316,10 +301,6 @@ public class OpenSSLSocketImpl
NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
- if (enabledCompressionMethods.length != 0) {
- NativeCrypto.setEnabledCompressionMethods(sslNativePointer,
- enabledCompressionMethods);
- }
if (useSessionTickets) {
NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
}
@@ -385,9 +366,11 @@ public class OpenSSLSocketImpl
}
// Temporarily use a different timeout for the handshake process
- int savedTimeoutMilliseconds = getSoTimeout();
+ int savedReadTimeoutMilliseconds = getSoTimeout();
+ int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
if (handshakeTimeoutMilliseconds >= 0) {
setSoTimeout(handshakeTimeoutMilliseconds);
+ setSoWriteTimeout(handshakeTimeoutMilliseconds);
}
int sslSessionNativePointer;
@@ -423,7 +406,8 @@ public class OpenSSLSocketImpl
// Restore the original timeout now that the handshake is complete
if (handshakeTimeoutMilliseconds >= 0) {
- setSoTimeout(savedTimeoutMilliseconds);
+ setSoTimeout(savedReadTimeoutMilliseconds);
+ setSoWriteTimeout(savedWriteTimeoutMilliseconds);
}
// if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
@@ -442,7 +426,7 @@ public class OpenSSLSocketImpl
}
}
- private String getPeerHostName() {
+ String getPeerHostName() {
if (wrappedHost != null) {
return wrappedHost;
}
@@ -453,7 +437,7 @@ public class OpenSSLSocketImpl
return null;
}
- private int getPeerPort() {
+ int getPeerPort() {
return wrappedHost == null ? super.getPort() : wrappedPort;
}
@@ -594,8 +578,13 @@ public class OpenSSLSocketImpl
}
boolean client = sslParameters.getUseClientMode();
if (client) {
- sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain,
- authMethod);
+ X509TrustManager x509tm = sslParameters.getTrustManager();
+ if (x509tm instanceof TrustManagerImpl) {
+ TrustManagerImpl tm = (TrustManagerImpl) x509tm;
+ tm.checkServerTrusted(peerCertificateChain, authMethod, wrappedHost);
+ } else {
+ x509tm.checkServerTrusted(peerCertificateChain, authMethod);
+ }
} else {
String authType = peerCertificateChain[0].getPublicKey().getAlgorithm();
sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain,
@@ -715,7 +704,7 @@ public class OpenSSLSocketImpl
return;
}
NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(),
- OpenSSLSocketImpl.this, buf, offset, byteCount);
+ OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds);
}
}
}
@@ -793,35 +782,6 @@ public class OpenSSLSocketImpl
}
/**
- * The names of the compression methods that may be used on this SSL
- * connection.
- * @return an array of compression methods
- */
- public String[] getSupportedCompressionMethods() {
- return NativeCrypto.getSupportedCompressionMethods();
- }
-
- /**
- * The names of the compression methods versions that are in use
- * on this SSL connection.
- *
- * @return an array of compression methods
- */
- public String[] getEnabledCompressionMethods() {
- return enabledCompressionMethods.clone();
- }
-
- /**
- * Enables compression methods listed by getSupportedCompressionMethods().
- *
- * @throws IllegalArgumentException when one or more of the names in the
- * array are not supported, or when the array is null.
- */
- public void setEnabledCompressionMethods(String[] methods) {
- enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods);
- }
-
- /**
* This method enables session ticket support.
*
* @param useSessionTickets True to enable session tickets
@@ -875,21 +835,42 @@ public class OpenSSLSocketImpl
throw new SocketException("Methods sendUrgentData, setOOBInline are not supported.");
}
- @Override public void setSoTimeout(int timeoutMilliseconds) throws SocketException {
- super.setSoTimeout(timeoutMilliseconds);
- this.timeoutMilliseconds = timeoutMilliseconds;
+ @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
+ super.setSoTimeout(readTimeoutMilliseconds);
+ this.readTimeoutMilliseconds = readTimeoutMilliseconds;
}
@Override public int getSoTimeout() throws SocketException {
- return timeoutMilliseconds;
+ return readTimeoutMilliseconds;
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+ this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
+
+ StructTimeval tv = StructTimeval.fromMillis(writeTimeoutMilliseconds);
+ try {
+ Libcore.os.setsockoptTimeval(getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ public int getSoWriteTimeout() throws SocketException {
+ return writeTimeoutMilliseconds;
}
/**
* Set the handshake timeout on this socket. This timeout is specified in
* milliseconds and will be used only during the handshake process.
*/
- public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException {
- this.handshakeTimeoutMilliseconds = timeoutMilliseconds;
+ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+ this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
}
@Override public void close() throws IOException {
@@ -914,12 +895,13 @@ public class OpenSSLSocketImpl
}
}
- NativeCrypto.SSL_interrupt(sslNativePointer);
-
synchronized (this) {
+
+ // Interrupt any outstanding reads or writes before taking the writeLock and readLock
+ NativeCrypto.SSL_interrupt(sslNativePointer);
+
synchronized (writeLock) {
synchronized (readLock) {
-
// Shut down the SSL connection, per se.
try {
if (handshakeStarted) {
diff --git a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java
index 07f64e4..8b74514 100644
--- a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,21 +14,16 @@
* limitations under the License.
*/
-package java.util.concurrent.locks;
+package org.apache.harmony.xnet.provider.jsse;
-import sun.misc.Unsafe;
+// public for testing by CertPinManagerTest
+public class PinEntryException extends Exception {
-/**
- * Easy access to {@link Unsafe} for the rest of this package.
- */
-/*package*/ final class UnsafeAccess {
- /** non-null; unique instance of {@link Unsafe} */
- /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe();
+ PinEntryException() {
+ }
- /**
- * This class is uninstantiable.
- */
- private UnsafeAccess() {
- // This space intentionally left blank.
+ PinEntryException(String msg) {
+ super(msg);
}
}
+
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java
new file mode 100644
index 0000000..40b1838
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import libcore.io.Base64;
+import libcore.io.DropBox;
+
+public class PinFailureLogger {
+
+ private static final long LOG_INTERVAL_NANOS = 1000 * 1000 * 1000 * 60 * 60;
+
+ private static long lastLoggedNanos = 0;
+
+ public static synchronized void log(String cn, boolean chainContainsUserCert,
+ boolean pinIsEnforcing,
+ List<X509Certificate> chain) {
+ // if we've logged recently, don't do it again
+ if (!timeToLog()) {
+ return;
+ }
+ // otherwise, log the event
+ writeToLog(cn, chainContainsUserCert, pinIsEnforcing, chain);
+ // update the last logged time
+ lastLoggedNanos = System.nanoTime();
+ }
+
+ protected static synchronized void writeToLog(String cn, boolean chainContainsUserCert,
+ boolean pinIsEnforcing,
+ List<X509Certificate> chain) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(cn);
+ sb.append("|");
+ sb.append(chainContainsUserCert);
+ sb.append("|");
+ sb.append(pinIsEnforcing);
+ sb.append("|");
+ for (X509Certificate cert : chain) {
+ try {
+ sb.append(Base64.encode(cert.getEncoded()));
+ } catch (CertificateEncodingException e) {
+ sb.append("Error: could not encode certificate");
+ }
+ sb.append("|");
+ }
+ DropBox.addText("cert_pin_failure", sb.toString());
+ }
+
+ protected static boolean timeToLog() {
+ long currentTimeNanos = System.nanoTime();
+ return ((currentTimeNanos - lastLoggedNanos) > LOG_INTERVAL_NANOS);
+ }
+}
+
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java
new file mode 100644
index 0000000..c05a391
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import libcore.io.EventLogger;
+
+/**
+ * This class represents a single entry in the pin file.
+ */
+// public for testing by CertPinManagerTest
+public class PinListEntry {
+
+ /** The Common Name (CN) as used on the SSL certificate */
+ private final String cn;
+
+ /**
+ * Determines whether a failed match here will prevent the chain from being accepted. If true,
+ * an unpinned chain will log and cause a match failure. If false, it will merely log.
+ */
+ private final boolean enforcing;
+
+ private final Set<String> pinnedFingerprints = new HashSet<String>();
+
+ private static final boolean DEBUG = false;
+
+ private final TrustedCertificateStore certStore;
+
+ public String getCommonName() {
+ return cn;
+ }
+
+ public boolean getEnforcing() {
+ return enforcing;
+ }
+
+ public PinListEntry(String entry, TrustedCertificateStore store) throws PinEntryException {
+ if (entry == null) {
+ throw new NullPointerException("entry == null");
+ }
+ certStore = store;
+ // Examples:
+ // *.google.com=true|34c8a0d...9e04ca05f,9e04ca05f...34c8a0d
+ // *.android.com=true|ca05f...8a0d34c
+ // clients.google.com=false|9e04ca05f...34c8a0d,34c8a0d...9e04ca05f
+ String[] values = entry.split("[=,|]");
+ // entry must have a CN, an enforcement value, and at least one pin
+ if (values.length < 3) {
+ throw new PinEntryException("Received malformed pin entry");
+ }
+ // get the cn
+ cn = values[0]; // is there more validation we can do here?
+ enforcing = enforcementValueFromString(values[1]);
+ // the remainder should be pins
+ addPins(Arrays.copyOfRange(values, 2, values.length));
+ }
+
+ private static boolean enforcementValueFromString(String val) throws PinEntryException {
+ if (val.equals("true")) {
+ return true;
+ } else if (val.equals("false")) {
+ return false;
+ } else {
+ throw new PinEntryException("Enforcement status is not a valid value");
+ }
+ }
+
+ /**
+ * Checks the given chain against the pin list corresponding to this entry.
+ *
+ * If the pin list does not contain the required certs and the enforcing field is true then
+ * this returns true, indicating a verification error. Otherwise, it returns false and
+ * verification should proceed.
+ */
+ public boolean chainIsNotPinned(List<X509Certificate> chain) {
+ for (X509Certificate cert : chain) {
+ String fingerprint = getFingerprint(cert);
+ if (pinnedFingerprints.contains(fingerprint)) {
+ return false;
+ }
+ }
+ logPinFailure(chain);
+ return enforcing;
+ }
+
+ private static String getFingerprint(X509Certificate cert) {
+ try {
+ MessageDigest dgst = MessageDigest.getInstance("SHA512");
+ byte[] encoded = cert.getPublicKey().getEncoded();
+ byte[] fingerprint = dgst.digest(encoded);
+ return IntegralToString.bytesToHexString(fingerprint, false);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private void addPins(String[] pins) {
+ for (String pin : pins) {
+ validatePin(pin);
+ }
+ Collections.addAll(pinnedFingerprints, pins);
+ }
+
+ private static void validatePin(String pin) {
+ // check to make sure the length is correct
+ if (pin.length() != 128) {
+ throw new IllegalArgumentException("Pin is not a valid length");
+ }
+ // check to make sure that it's a valid hex string
+ try {
+ new BigInteger(pin, 16);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Pin is not a valid hex string", e);
+ }
+ }
+
+ private boolean chainContainsUserCert(List<X509Certificate> chain) {
+ if (certStore == null) {
+ return false;
+ }
+ for (X509Certificate cert : chain) {
+ if (certStore.isUserAddedCertificate(cert)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void logPinFailure(List<X509Certificate> chain) {
+ PinFailureLogger.log(cn, chainContainsUserCert(chain), enforcing, chain);
+ }
+}
+
diff --git a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java
index 96fff17..74b3c65 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,21 +14,19 @@
* limitations under the License.
*/
-package java.util.concurrent.atomic;
+package org.apache.harmony.xnet.provider.jsse;
-import sun.misc.Unsafe;
+class PinManagerException extends Exception {
-/**
- * Easy access to {@link Unsafe} for the rest of this package.
- */
-/*package*/ final class UnsafeAccess {
- /** non-null; unique instance of {@link Unsafe} */
- /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe();
+ PinManagerException() {
+ }
- /**
- * This class is uninstantiable.
- */
- private UnsafeAccess() {
- // This space intentionally left blank.
+ PinManagerException(String msg) {
+ super(msg);
+ }
+
+ PinManagerException(String msg, Exception e) {
+ super(msg, e);
}
}
+
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
index be9a7fc..93496cf 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
@@ -88,7 +88,7 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory {
if (instantiationException != null) {
throw instantiationException;
}
- return new SSLSocketWrapper(s, autoClose, (SSLParametersImpl) sslParameters
+ return new SSLSocketWrapper(s, host, port, autoClose, (SSLParametersImpl) sslParameters
.clone());
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java
index 6e5fddd..2cd2cf5 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java
@@ -41,6 +41,10 @@ public class SSLSocketImpl extends SSLSocket {
// indicates if handshake has been started
private boolean handshake_started = false;
+ // used when we're wrapping a socket
+ private final String wrappedHost;
+ private final int wrappedPort;
+
// record protocol to be used
protected SSLRecordProtocol recordProtocol;
// handshake protocol to be used
@@ -83,6 +87,8 @@ public class SSLSocketImpl extends SSLSocket {
*/
protected SSLSocketImpl(SSLParametersImpl sslParameters) {
this.sslParameters = sslParameters;
+ this.wrappedHost = null;
+ this.wrappedPort = -1;
// init should be called after creation!
}
@@ -99,6 +105,8 @@ public class SSLSocketImpl extends SSLSocket {
protected SSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
throws IOException, UnknownHostException {
super(host, port);
+ this.wrappedHost = host;
+ this.wrappedPort = port;
this.sslParameters = sslParameters;
init();
}
@@ -120,6 +128,8 @@ public class SSLSocketImpl extends SSLSocket {
SSLParametersImpl sslParameters) throws IOException,
UnknownHostException {
super(host, port, localHost, localPort);
+ this.wrappedHost = host;
+ this.wrappedPort = port;
this.sslParameters = sslParameters;
init();
}
@@ -138,6 +148,8 @@ public class SSLSocketImpl extends SSLSocket {
SSLParametersImpl sslParameters) throws IOException {
super(host, port);
this.sslParameters = sslParameters;
+ this.wrappedHost = null;
+ this.wrappedPort = -1;
init();
}
@@ -158,6 +170,8 @@ public class SSLSocketImpl extends SSLSocket {
SSLParametersImpl sslParameters) throws IOException {
super(address, port, localAddress, localPort);
this.sslParameters = sslParameters;
+ this.wrappedHost = null;
+ this.wrappedPort = -1;
init();
}
@@ -193,6 +207,29 @@ public class SSLSocketImpl extends SSLSocket {
}
}
+ String getWrappedHostName() {
+ return wrappedHost;
+ }
+
+ int getWrappedPort() {
+ return wrappedPort;
+ }
+
+ String getPeerHostName() {
+ if (wrappedHost != null) {
+ return wrappedHost;
+ }
+ InetAddress inetAddress = super.getInetAddress();
+ if (inetAddress != null) {
+ return inetAddress.getHostName();
+ }
+ return null;
+ }
+
+ int getPeerPort() {
+ return (wrappedPort == -1) ? super.getPort() : wrappedPort;
+ }
+
// --------------- SSLParameters based methods ---------------------
/**
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java
index 27bbead..a393e24 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java
@@ -32,8 +32,9 @@ public class SSLSocketWrapper extends SSLSocketImpl {
private final Socket socket;
private final boolean autoClose;
- protected SSLSocketWrapper(Socket socket, boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
- super(sslParameters);
+ protected SSLSocketWrapper(Socket socket, String host, int port, boolean autoClose,
+ SSLParametersImpl sslParameters) throws IOException {
+ super(host, port, sslParameters);
if (!socket.isConnected()) {
throw new SocketException("Socket is not connected.");
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
index c5e1838..fa8d291 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
@@ -19,7 +19,6 @@ package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.AccessController;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -659,7 +658,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol {
} else {
if ((parameters.getNeedClientAuth() && clientCert == null)
|| clientKeyExchange == null
- || (clientCert != null
+ || (clientCert != null && clientCert.certs.length > 0
&& !clientKeyExchange.isEmpty()
&& certificateVerify == null)) {
unexpectedMessage();
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
index 3f362c5..0218249 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
@@ -35,6 +35,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.X509TrustManager;
+import libcore.io.EventLogger;
/**
*
@@ -52,6 +53,11 @@ public final class TrustManagerImpl implements X509TrustManager {
private final KeyStore rootKeyStore;
/**
+ * The CertPinManager, which validates the chain against a host-to-pin mapping
+ */
+ private CertPinManager pinManager;
+
+ /**
* The backing store for the AndroidCAStore if non-null. This will
* be null when the rootKeyStore is null, implying we are not
* using the AndroidCAStore.
@@ -83,6 +89,13 @@ public final class TrustManagerImpl implements X509TrustManager {
* @param ks
*/
public TrustManagerImpl(KeyStore keyStore) {
+ this(keyStore, null);
+ }
+
+ /**
+ * For testing only
+ */
+ public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) {
CertPathValidator validatorLocal = null;
CertificateFactory factoryLocal = null;
KeyStore rootKeyStoreLocal = null;
@@ -111,6 +124,17 @@ public final class TrustManagerImpl implements X509TrustManager {
} catch (Exception e) {
errLocal = e;
}
+
+ if (manager != null) {
+ this.pinManager = manager;
+ } else {
+ try {
+ pinManager = new CertPinManager(trustedCertificateStoreLocal);
+ } catch (PinManagerException e) {
+ throw new SecurityException("Could not initialize CertPinManager", e);
+ }
+ }
+
this.rootKeyStore = rootKeyStoreLocal;
this.trustedCertificateStore = trustedCertificateStoreLocal;
this.validator = validatorLocal;
@@ -155,12 +179,22 @@ public final class TrustManagerImpl implements X509TrustManager {
@Override public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- checkTrusted(chain, authType);
+ checkTrusted(chain, authType, null);
}
@Override public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- checkTrusted(chain, authType);
+ checkTrusted(chain, authType, null);
+ }
+
+ /**
+ * Validates whether a server is trusted. If hostname is given and non-null it also checks if
+ * chain is pinned appropriately for that host. If null, it does not check for pinned certs.
+ * The return value is a list of the certificates used for making the trust decision.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
+ String host) throws CertificateException {
+ return checkTrusted(chain, authType, host);
}
public void handleTrustStorageUpdate() {
@@ -168,10 +202,10 @@ public final class TrustManagerImpl implements X509TrustManager {
trustedCertificateIndex.reset();
} else {
trustedCertificateIndex.reset(trustAnchors(acceptedIssuers));
- }
+ }
}
- private void checkTrusted(X509Certificate[] chain, String authType)
+ private List<X509Certificate> checkTrusted(X509Certificate[] chain, String authType, String host)
throws CertificateException {
if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) {
throw new IllegalArgumentException("null or zero-length parameter");
@@ -180,21 +214,71 @@ public final class TrustManagerImpl implements X509TrustManager {
throw new CertificateException(err);
}
- Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
- X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchors);
+ // get the cleaned up chain and trust anchor
+ Set<TrustAnchor> trustAnchor = new HashSet<TrustAnchor>(); // there can only be one!
+ X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchor);
+
+ // add the first trust anchor to the chain, which may be an intermediate
+ List<X509Certificate> wholeChain = new ArrayList<X509Certificate>();
+ wholeChain.addAll(Arrays.asList(newChain));
+ // trustAnchor is actually just a single element
+ for (TrustAnchor trust : trustAnchor) {
+ wholeChain.add(trust.getTrustedCert());
+ }
+
+ // add all the cached certificates from the cert index, avoiding loops
+ // this gives us a full chain from leaf to root, which we use for cert pinning and pass
+ // back out to callers when we return.
+ X509Certificate last = wholeChain.get(wholeChain.size() - 1);
+ while (true) {
+ TrustAnchor cachedTrust = trustedCertificateIndex.findByIssuerAndSignature(last);
+ // the cachedTrust can be null if there isn't anything in the index or if a user has
+ // trusted a non-self-signed cert.
+ if (cachedTrust == null) {
+ break;
+ }
+
+ // at this point we have a cached trust anchor, but don't know if its one we got from
+ // the server. Extract the cert, compare it to the last element in the chain, and add it
+ // if we haven't seen it before.
+ X509Certificate next = cachedTrust.getTrustedCert();
+ if (next != last) {
+ wholeChain.add(next);
+ last = next;
+ } else {
+ // if next == last then we found a self-signed cert and the chain is done
+ break;
+ }
+ }
+
+ // build the cert path from the array of certs sans trust anchors
+ CertPath certPath = factory.generateCertPath(Arrays.asList(newChain));
+
+ if (host != null) {
+ boolean chainIsNotPinned = true;
+ try {
+ chainIsNotPinned = pinManager.chainIsNotPinned(host, wholeChain);
+ } catch (PinManagerException e) {
+ throw new CertificateException(e);
+ }
+ if (chainIsNotPinned) {
+ throw new CertificateException(new CertPathValidatorException(
+ "Certificate path is not properly pinned.", null, certPath, -1));
+ }
+ }
+
if (newChain.length == 0) {
// chain was entirely trusted, skip the validator
- return;
+ return wholeChain;
}
- CertPath certPath = factory.generateCertPath(Arrays.asList(newChain));
- if (trustAnchors.isEmpty()) {
+ if (trustAnchor.isEmpty()) {
throw new CertificateException(new CertPathValidatorException(
"Trust anchor for certification path not found.", null, certPath, -1));
}
try {
- PKIXParameters params = new PKIXParameters(trustAnchors);
+ PKIXParameters params = new PKIXParameters(trustAnchor);
params.setRevocationEnabled(false);
validator.validate(certPath, params);
// Add intermediate CAs to the index to tolerate sites
@@ -211,6 +295,8 @@ public final class TrustManagerImpl implements X509TrustManager {
} catch (CertPathValidatorException e) {
throw new CertificateException(e);
}
+
+ return wholeChain;
}
/**
@@ -232,17 +318,9 @@ public final class TrustManagerImpl implements X509TrustManager {
// Start with the first certificate in the chain, assuming it
// is the leaf certificate (server or client cert).
for (currIndex = 0; currIndex < chain.length; currIndex++) {
- // If the current cert is a TrustAnchor, we can ignore the rest of the chain.
- // This avoids including "bridge" CA certs that added for legacy compatability.
- TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[currIndex]);
- if (trustAnchor != null) {
- trustAnchors.add(trustAnchor);
- currIndex--;
- break;
- }
- // Walk the rest of the chain to find a "subject" matching
+ // Walk the chain to find a "subject" matching
// the "issuer" of the current certificate. In a properly
- // order chain this should be the next cert and be fast.
+ // ordered chain this should be the next cert and be fast.
// If not, we reorder things to be as the validator will
// expect.
boolean foundNext = false;
@@ -271,15 +349,27 @@ public final class TrustManagerImpl implements X509TrustManager {
}
}
- // 2. If the chain is now shorter, copy to an appropriately sized array.
- int chainLength = currIndex + 1;
+ // 2. Find the trust anchor in the chain, if any
+ int anchorIndex;
+ for (anchorIndex = 0; anchorIndex < chain.length; anchorIndex++) {
+ // If the current cert is a TrustAnchor, we can ignore the rest of the chain.
+ // This avoids including "bridge" CA certs that added for legacy compatibility.
+ TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[anchorIndex]);
+ if (trustAnchor != null) {
+ trustAnchors.add(trustAnchor);
+ break;
+ }
+ }
+
+ // 3. If the chain is now shorter, copy to an appropriately sized array.
+ int chainLength = anchorIndex;
X509Certificate[] newChain = ((chainLength == chain.length)
? chain
: Arrays.copyOf(chain, chainLength));
- // 3. If no TrustAnchor was found in cleanup, look for one now
+ // 4. If we didn't find a trust anchor earlier, look for one now
if (trustAnchors.isEmpty()) {
- TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[chainLength-1]);
+ TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[anchorIndex-1]);
if (trustAnchor != null) {
trustAnchors.add(trustAnchor);
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
index 54116a7..e7b1a7c 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
@@ -16,6 +16,11 @@
package org.apache.harmony.xnet.provider.jsse;
+import org.apache.harmony.security.x501.Name;
+import org.apache.harmony.security.x509.AuthorityKeyIdentifier;
+import org.apache.harmony.security.x509.GeneralName;
+import org.apache.harmony.security.x509.GeneralNames;
+import org.apache.harmony.security.x509.SubjectKeyIdentifier;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -23,19 +28,20 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.KeyStoreSpi;
-import java.security.PublicKey;
-import java.security.cert.CertSelector;
+import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import libcore.io.IoUtils;
+import libcore.util.Objects;
/**
* A source for trusted root certificate authority (CA) certificates
@@ -292,6 +298,14 @@ public final class TrustedCertificateStore {
}
/**
+ * Returns true to indicate that the certificate was added by the
+ * user, false otherwise.
+ */
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ return getCertificateFile(addedDir, cert).exists();
+ }
+
+ /**
* Returns a File for where the certificate is found if it exists
* or where it should be installed if it does not exist. The
* caller can disambiguate these cases by calling {@code
@@ -366,6 +380,109 @@ public final class TrustedCertificateStore {
return null;
}
+ private static AuthorityKeyIdentifier getAuthorityKeyIdentifier(X509Certificate cert) {
+ final byte[] akidBytes = cert.getExtensionValue("2.5.29.35");
+ if (akidBytes == null) {
+ return null;
+ }
+
+ try {
+ return AuthorityKeyIdentifier.decode(akidBytes);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static SubjectKeyIdentifier getSubjectKeyIdentifier(X509Certificate cert) {
+ final byte[] skidBytes = cert.getExtensionValue("2.5.29.14");
+ if (skidBytes == null) {
+ return null;
+ }
+
+ try {
+ return SubjectKeyIdentifier.decode(skidBytes);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean isSelfSignedCertificate(X509Certificate cert) {
+ if (!Objects.equal(cert.getSubjectX500Principal(), cert.getIssuerX500Principal())) {
+ return false;
+ }
+
+ final AuthorityKeyIdentifier akid = getAuthorityKeyIdentifier(cert);
+ if (akid != null) {
+ final byte[] akidKeyId = akid.getKeyIdentifier();
+ if (akidKeyId != null) {
+ final SubjectKeyIdentifier skid = getSubjectKeyIdentifier(cert);
+ if (!Arrays.equals(akidKeyId, skid.getKeyIdentifier())) {
+ return false;
+ }
+ }
+
+ final BigInteger akidSerial = akid.getAuthorityCertSerialNumber();
+ if (akidSerial != null && !akidSerial.equals(cert.getSerialNumber())) {
+ return false;
+ }
+
+ final GeneralNames possibleIssuerNames = akid.getAuthorityCertIssuer();
+ if (possibleIssuerNames != null) {
+ GeneralName issuerName = null;
+
+ /* Get the first Directory Name (DN) to match how OpenSSL works. */
+ for (GeneralName possibleName : possibleIssuerNames.getNames()) {
+ if (possibleName.getTag() == GeneralName.DIR_NAME) {
+ issuerName = possibleName;
+ break;
+ }
+ }
+
+ if (issuerName != null) {
+ final String issuerCanonical = ((Name) issuerName.getName())
+ .getName(X500Principal.CANONICAL);
+
+ try {
+ final String subjectCanonical = new Name(cert.getSubjectX500Principal()
+ .getEncoded()).getName(X500Principal.CANONICAL);
+ if (!issuerCanonical.equals(subjectCanonical)) {
+ return false;
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to build a certificate chain from the supplied {@code leaf}
+ * argument through the chain of issuers as high up as known. If the chain
+ * can't be completed, the most complete chain available will be returned.
+ * This means that a list with only the {@code leaf} certificate is returned
+ * if no issuer certificates could be found.
+ */
+ public List<X509Certificate> getCertificateChain(X509Certificate leaf) {
+ final List<X509Certificate> chain = new ArrayList<X509Certificate>();
+ chain.add(leaf);
+
+ for (int i = 0; true; i++) {
+ X509Certificate cert = chain.get(i);
+ if (isSelfSignedCertificate(cert)) {
+ break;
+ }
+ X509Certificate issuer = findIssuer(cert);
+ if (issuer == null) {
+ break;
+ }
+ chain.add(issuer);
+ }
+
+ return chain;
+ }
+
// like java.security.cert.CertSelector but with X509Certificate and without cloning
private static interface CertSelector {
public boolean match(X509Certificate cert);