OpenJDK / portola / portola
changeset 8198:aca2f99e4b52
Merge
author | herrick |
---|---|
date | Wed, 09 Feb 2011 09:32:04 -0500 |
parents | e45f21c2a40b f4e3f61c9587 |
children | bbe30e093ae9 |
files | jdk/make/java/hpi/Makefile jdk/make/java/hpi/hpi_common.gmk jdk/make/java/hpi/native/Makefile jdk/make/java/hpi/native/mapfile-vers jdk/make/java/hpi/native/reorder-i586 jdk/make/java/hpi/native/reorder-sparc jdk/make/java/hpi/native/reorder-sparcv9 jdk/make/java/hpi/windows/Makefile jdk/src/share/hpi/export/bool.h jdk/src/share/hpi/export/dll.h jdk/src/share/hpi/export/hpi.h jdk/src/share/hpi/include/hpi_impl.h jdk/src/share/hpi/include/vm_calls.h jdk/src/share/hpi/src/hpi.c jdk/src/solaris/classes/sun/awt/fontconfigs/solaris.fontconfig.5.8.properties jdk/src/solaris/classes/sun/awt/fontconfigs/solaris.fontconfig.5.9.properties jdk/src/solaris/hpi/export/byteorder_md.h jdk/src/solaris/hpi/export/hpi_md.h jdk/src/solaris/hpi/export/io_md.h jdk/src/solaris/hpi/export/path_md.h jdk/src/solaris/hpi/export/timeval_md.h jdk/src/solaris/hpi/include/hpi_init.h jdk/src/solaris/hpi/include/interrupt.h jdk/src/solaris/hpi/include/largefile.h jdk/src/solaris/hpi/include/largefile_linux.h jdk/src/solaris/hpi/include/largefile_solaris.h jdk/src/solaris/hpi/native_threads/include/condvar_md.h jdk/src/solaris/hpi/native_threads/include/monitor_md.h jdk/src/solaris/hpi/native_threads/include/mutex_md.h jdk/src/solaris/hpi/native_threads/include/np.h jdk/src/solaris/hpi/native_threads/include/porting.h jdk/src/solaris/hpi/native_threads/include/threads_md.h jdk/src/solaris/hpi/native_threads/src/condvar_md.c jdk/src/solaris/hpi/native_threads/src/interrupt_md.c jdk/src/solaris/hpi/native_threads/src/monitor_md.c jdk/src/solaris/hpi/native_threads/src/mutex_md.c jdk/src/solaris/hpi/native_threads/src/sys_api_td.c jdk/src/solaris/hpi/native_threads/src/threads_linux.c jdk/src/solaris/hpi/native_threads/src/threads_md.c jdk/src/solaris/hpi/native_threads/src/threads_solaris.c jdk/src/solaris/hpi/src/interrupt.c jdk/src/solaris/hpi/src/linker_md.c jdk/src/solaris/hpi/src/memory_md.c jdk/src/solaris/hpi/src/system_md.c jdk/src/windows/hpi/export/byteorder_md.h jdk/src/windows/hpi/export/hpi_md.h jdk/src/windows/hpi/export/io_md.h jdk/src/windows/hpi/export/path_md.h jdk/src/windows/hpi/export/timeval_md.h jdk/src/windows/hpi/include/monitor_md.h jdk/src/windows/hpi/include/mutex_md.h jdk/src/windows/hpi/include/threads_md.h jdk/src/windows/hpi/src/linker_md.c jdk/src/windows/hpi/src/memory_md.c jdk/src/windows/hpi/src/monitor_md.c jdk/src/windows/hpi/src/path_md.c jdk/src/windows/hpi/src/socket_md.c jdk/src/windows/hpi/src/sys_api_md.c jdk/src/windows/hpi/src/system_md.c jdk/src/windows/hpi/src/threads_md.c jdk/test/java/net/InetAddress/B4762344.java jdk/test/java/net/InetAddress/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor jdk/test/java/net/InetAddress/Simple1NameServiceDescriptor.java jdk/test/java/net/InetAddress/Simple2NameServiceDescriptor.java jdk/test/java/net/InetAddress/SimpleNameService.java jdk/test/sun/net/InetAddress/nameservice/B6442088.java jdk/test/sun/net/InetAddress/nameservice/CacheTest.java jdk/test/sun/net/InetAddress/nameservice/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor jdk/test/sun/net/InetAddress/nameservice/SimpleNameService.java jdk/test/sun/net/InetAddress/nameservice/SimpleNameServiceDescriptor.java |
diffstat | 337 files changed, 9048 insertions(+), 16071 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/.hgtags Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/.hgtags Wed Feb 09 09:32:04 2011 -0500 @@ -101,3 +101,4 @@ 1c72adc9d5f331cb882cf5354ba0dcb118a60b23 jdk7-b124 0a56bdd709d01c1663047e55201d19152ffd3d69 jdk7-b125 8361ef97a0f90086c9048beaf7cea1a37216c4cd jdk7-b126 +29e09de1d0b4f84faea114cf10b3ec08b59acc4e jdk7-b127
--- a/jdk/make/common/Defs-linux.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Defs-linux.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -75,15 +75,6 @@ CC_PROGRAM_OUTPUT_FLAG = -o #trailing blank required! # -# Default HPI libraries. Build will build only native, unless -# overriden at the make command line. This makes it convenient for -# people doing, say, a pthreads port -- they can create a posix -# directory here, and say "gnumake HPIS=posix" at the top -# level. -# -HPIS = native - -# # Default optimization #
--- a/jdk/make/common/Defs-solaris.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Defs-solaris.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -75,15 +75,6 @@ CC_PROGRAM_OUTPUT_FLAG = -o #trailing blank required! # -# Default HPI libraries. Build will build only native unless -# overriden at the make command line. This makes it convenient for -# people doing, say, a pthreads port -- they can create a posix -# directory here, and say "gnumake HPIS=posix" at the top -# level. -# -HPIS = native - -# # Java default optimization (-x04/-O2) etc. Applies to the VM. # ifndef OPTIMIZATION_LEVEL
--- a/jdk/make/common/Defs-windows.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Defs-windows.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -43,7 +43,6 @@ # The suffix applied to scripts (.bat for windows, nothing for unix) SCRIPT_SUFFIX = .bat -HPIS = windows # LIB_LOCATION, which for windows identifies where .exe files go, may be # set by each GNUmakefile. The default is BINDIR. ifndef LIB_LOCATION @@ -365,10 +364,6 @@ # LFLAGS are the flags given to $(LINK) and used to build the actual DLL file BASELFLAGS = -nologo /opt:REF /incremental:no -ifdef MT - # VS2005, VS2008, and beyond: ask LINK to generate manifests for .dll & .exe - BASELFLAGS += /manifest -endif LFLAGS = $(BASELFLAGS) $(LDEBUG) $(EXTRA_LFLAGS) $(LFLAGS_$(COMPILER_VERSION)) LDDFLAGS += $(LFLAGS_$(COMPILER_VERSION))
--- a/jdk/make/common/Defs.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Defs.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -271,10 +271,9 @@ # An attempt is made to generate unique enough directories for the # generated files to not have name collisisons. Most build units # defines PRODUCT (except Release.gmk), but then they may or may -# not define PACKAGE, THREADIR (only HPI uses this), PROGRAM, and -# LIBRARY. This code chunk attempts to generate a unique -# OBJDIR/CLASSHDRDIR for each build unit based on which of those -# values are set within each build unit. +# not define PACKAGE, PROGRAM, and LIBRARY. This code attempts to +# generate a unique OBJDIR/CLASSHDRDIR for each build unit based +# on which of those values are set within each build unit. UNIQUE_LOCATION_STRING = tmp @@ -298,10 +297,6 @@ endif endif -ifneq ($(THREADDIR),) - UNIQUE_LOCATION_STRING += /$(THREADDIR) -endif - # # Build units may or may not define MODULE. Default to "other". #
--- a/jdk/make/common/Demo.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Demo.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -73,8 +73,11 @@ # Destination "src" directory DEMO_BUILD_SRCDIR = $(DEMO_BUILD_AREA)/src -DEMO_BUILD_SRCZIP = $(DEMO_BUILD_AREA)/src.zip -DEMO_SOURCE_ZIP = $(DEMO_DESTDIR)/src.zip + +ifndef DEMO_SKIP_SRCZIP + DEMO_BUILD_SRCZIP = $(DEMO_BUILD_AREA)/src.zip + DEMO_SOURCE_ZIP = $(DEMO_DESTDIR)/src.zip +endif # Place to hold the jar image we are creating DEMO_JAR_IMAGE = $(DEMO_BUILD_AREA)/jar_image @@ -258,14 +261,16 @@ endif -# Create a src.zip file -$(DEMO_BUILD_SRCZIP): $(DEMO_FULL_SOURCES) +ifndef DEMO_SKIP_SRCZIP + # Create a src.zip file + $(DEMO_BUILD_SRCZIP): $(DEMO_FULL_SOURCES) @$(prep-target) $(CD) $(DEMO_BUILD_AREA)/src && $(ZIPEXE) -q -r ../$(@F) . -# Install the destination src.zip file and create the src tree -$(DEMO_SOURCE_ZIP): $(DEMO_BUILD_SRCZIP) + # Install the destination src.zip file and create the src tree + $(DEMO_SOURCE_ZIP): $(DEMO_BUILD_SRCZIP) $(install-file) +endif # Native library building ifdef DEMO_LIBRARY @@ -362,7 +367,7 @@ $(RM) -r $(DEMO_BUILD_AREA) $(RM) -r $(DEMO_DESTDIR) -# This should not be needed, but some versions of GNU amke have a bug that +# This should not be needed, but some versions of GNU make have a bug that # sometimes deleted these files for some strange and unknown reason # (GNU make version 3.78.1 has the problem, GNU make version 3.80 doesn't?) .PRECIOUS: $(DEMO_FULL_SOURCES) $(DEMO_BUILD_SRCZIP) $(DEMO_SOURCE_ZIP)
--- a/jdk/make/common/Library.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Library.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -159,9 +159,6 @@ # build it into $(OBJDIR) so that the other generated files get put # there, then copy just the DLL (and MAP file) to the requested directory. # -# In VS2005 or VS2008 the link command creates a .manifest file that we want -# to insert into the linked artifact so we do not need to track it separately. -# Use ";#2" for .dll and ";#1" for .exe in the MT command below: $(ACTUAL_LIBRARY):: $(OBJDIR)/$(LIBRARY).lcf @$(prep-target) @$(MKDIR) -p $(OBJDIR) @@ -169,9 +166,6 @@ -map:$(OBJDIR)/$(LIBRARY).map \ $(LFLAGS) @$(OBJDIR)/$(LIBRARY).lcf \ $(OTHER_LCF) $(JAVALIB) $(LDLIBS) -ifdef MT - $(MT) /manifest $(OBJDIR)/$(@F).manifest /outputresource:$(OBJDIR)/$(@F);#2 -endif $(CP) $(OBJDIR)/$(@F) $@ $(install-module-file) $(CP) $(OBJDIR)/$(LIBRARY).map $(@D)
--- a/jdk/make/common/Modules.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Modules.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -222,7 +222,7 @@ @# Remove certain *.lib files $(CD) $(JRE_MODULE_IMAGE_DIR)/lib && \ $(RM) java.$(LIB_SUFFIX) jvm.$(LIB_SUFFIX) \ - hpi.$(LIB_SUFFIX) awt.$(LIB_SUFFIX) jawt.$(LIB_SUFFIX) + awt.$(LIB_SUFFIX) jawt.$(LIB_SUFFIX) ifeq ($(ARCH_DATA_MODEL), 32) @# The Java Kernel JRE image ships with a special VM. It is not included @# in the full JRE image, so remove it. Also, is it only for 32-bit windows. @@ -415,8 +415,7 @@ trim-module-image-jdk:: @# Remove tools that should not be part of SDK. for t in $(NOTJDKTOOLS); do \ - $(RM) $(JDK_MODULE_IMAGE_DIR)/bin/$${t}$(EXE_SUFFIX) \ - $(JDK_MODULE_IMAGE_DIR)/bin/*/native_threads/$${t}$(EXE_SUFFIX); \ + $(RM) $(JDK_MODULE_IMAGE_DIR)/bin/$${t}$(EXE_SUFFIX); \ done # Get list of Elf files in the jdk
--- a/jdk/make/common/Program.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Program.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -142,10 +142,15 @@ STACK_SIZE=1048576 endif -# In VS2005 or VS2008 the link command creates a .manifest file that we want -# to insert into the linked artifact so we do not need to track it separately. +IMVERSION=$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION).$(JDK_UPDATE_VER).$(COOKED_BUILD_NUMBER) +$(OBJDIR)/$(PROGRAM).exe.manifest: $(JDK_TOPDIR)/src/windows/resource/java.manifest + @$(prep-target) + $(SED) 's%IMVERSION%$(IMVERSION)%g;s%PROGRAM%$(PROGRAM)%g' $< > $@ + +# We used a hand-crafted manifest file for all executables. +# It is tweaked to embed the build number and executable name. # Use ";#2" for .dll and ";#1" for .exe in the MT command below: -$(OBJDIR)/$(PROGRAM)$(EXE_SUFFIX):: $(OBJDIR)/$(PROGRAM).lcf $(FILES_o) $(JLI_LCF) +$(OBJDIR)/$(PROGRAM)$(EXE_SUFFIX):: $(OBJDIR)/$(PROGRAM).lcf $(FILES_o) $(JLI_LCF) $(OBJDIR)/$(PROGRAM).exe.manifest @$(prep-target) @set -- $?; \ $(ECHO) Rebuilding $@ because of $$1 $$2 $$3 $$4 $$5 $$6 $${7:+...};
--- a/jdk/make/common/Release.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/common/Release.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -133,7 +133,6 @@ # absolute directory names: note, these must exist prior to build # time - they are created in the main Makefile. JRE_IMAGE_BINDIR = $(JRE_IMAGE_DIR)/bin -JRE_IMAGE_THREADIR = $(JRE_IMAGE_DIR)/bin/*/native_threads MAINMANIFEST = $(JDK_TOPDIR)/make/tools/manifest.mf BEANMANIFEST = $(JDK_TOPDIR)/make/javax/swing/beaninfo/manifest @@ -802,7 +801,7 @@ @# Remove certain *.lib files $(CD) $(JRE_IMAGE_DIR)/lib && \ $(RM) java.$(LIB_SUFFIX) jvm.$(LIB_SUFFIX) \ - hpi.$(LIB_SUFFIX) awt.$(LIB_SUFFIX) jawt.$(LIB_SUFFIX) + awt.$(LIB_SUFFIX) jawt.$(LIB_SUFFIX) ifeq ($(ARCH_DATA_MODEL), 32) @# The Java Kernel JRE image ships with a special VM. It is not included @# in the full JRE image, so remove it. Also, is it only for 32-bit windows. @@ -1089,8 +1088,7 @@ trim-image-jdk:: @# Remove tools that should not be part of SDK. for t in $(NOTJDKTOOLS); do \ - $(RM) $(JDK_IMAGE_DIR)/bin/$${t}$(EXE_SUFFIX) \ - $(JDK_IMAGE_DIR)/bin/*/native_threads/$${t}$(EXE_SUFFIX); \ + $(RM) $(JDK_IMAGE_DIR)/bin/$${t}$(EXE_SUFFIX); \ done # Get list of Elf files in the jdk
--- a/jdk/make/java/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/java/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -34,7 +34,7 @@ # # The order of subdirs here is important # -SUBDIRS += hpi version jvm redist verify fdlibm java sun_nio jli main zip +SUBDIRS += version jvm redist verify fdlibm java sun_nio jli main zip # Others # Note: java_crw_demo java_hprof_demo are demos but must be delivered built in sdk
--- a/jdk/make/java/fdlibm/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/java/fdlibm/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -24,7 +24,7 @@ # # -# Makefile for native threads HPI. +# Makefile for fdlibm # # Note: # The fdlibm libraries are built using special rules in Library.gmk.
--- a/jdk/make/java/hpi/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -# -# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# Build HPI (Host Porting Interface) libraries -# - -BUILDDIR = ../.. -include $(BUILDDIR)/common/Defs.gmk - -# -# Build specified the HPI implementations -# -SUBDIRS = $(HPIS) -include $(BUILDDIR)/common/Subdirs.gmk - -all build clean clobber:: - $(SUBDIRS-loop) -
--- a/jdk/make/java/hpi/hpi_common.gmk Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -# -# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# Shared files between the different threads types on Solaris. Be -# careful when including this, you must get your variables right. -# - -# -# Common files on Solaris. -# -ifneq ($(PLATFORM), windows) -FILES_c += \ - interrupt.c \ - linker_md.c \ - memory_md.c \ - system_md.c \ - hpi.c -endif - -# -# Include paths can also be shared. -# -ifneq ($(PLATFORM), windows) -OTHER_INCLUDES += \ - -I$(PLATFORM_SRC)/hpi/$(THREADDIR)/include \ - -I$(PLATFORM_SRC)/hpi/include \ - -I$(PLATFORM_SRC)/hpi/export \ - -I$(SHARE_SRC)/hpi/include \ - -I$(SHARE_SRC)/hpi/export -else -OTHER_INCLUDES += \ - -I$(PLATFORM_SRC)/hpi/include \ - -I$(PLATFORM_SRC)/hpi/export \ - -I$(SHARE_SRC)/hpi/include \ - -I$(SHARE_SRC)/hpi/export -endif - -# -# Add to the default C and assembly file search paths. Clear any initial -# vpath settings to ensure that we don't look in unexpected places for HPI -# files. -# -vpath %.c -vpath %.c $(PLATFORM_SRC)/hpi/$(THREADDIR)/src -vpath %.c $(PLATFORM_SRC)/hpi/src -vpath %.c $(SHARE_SRC)/hpi/src - -vpath %.s -vpath %.s $(PLATFORM_SRC)/hpi/$(THREADDIR)/src -vpath %.s $(PLATFORM_SRC)/hpi/src - -# -# By default leave out locking statistics -# -ifneq ($(PLATFORM), windows) -LOCKSTATS = false -ifeq ($(LOCKSTATS), true) - CFLAGS_COMMON += -DLOCKSTATS -endif -endif - -# -# Things that must be linked in. -# -ifneq ($(PLATFORM), windows) -OTHER_LDLIBS += $(LIBSOCKET) $(LIBNSL) $(LIBM) -ldl -endif
--- a/jdk/make/java/hpi/native/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -# -# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# Makefile for native threads HPI. -# - -BUILDDIR = ../../.. -MODULE = base -LIBRARY = hpi -PRODUCT = java -THREADDIR = native_threads -LIB_LOCATION = $(LIBDIR)/$(LIBARCH)/$(THREADDIR) -include $(BUILDDIR)/common/Defs.gmk - -# -# Native threads specific C and .s files. -# -FILES_c = \ - monitor_md.c \ - threads_md.c \ - condvar_md.c \ - interrupt_md.c \ - mutex_md.c \ - sys_api_td.c \ - threads_$(PLATFORM).c - -# -# Other files/flags shared between the HPIs. -# -include $(BUILDDIR)/java/hpi/hpi_common.gmk - -# -# Rules for the .so file. -# -ifeq ($(PLATFORM), solaris) - ifneq ($(ARCH), amd64) - FILES_reorder += reorder-$(ARCH) - endif -endif -include $(BUILDDIR)/common/Mapfile-vers.gmk -include $(BUILDDIR)/common/Library.gmk - -# -# HPI flags for native threads. -# -OTHER_CPPFLAGS += -D_REENTRANT -DNATIVE - -ifeq ($(USE_PTHREADS),true) -OTHER_CPPFLAGS += -DUSE_PTHREADS -ifeq ($(MOOT_PRIORITIES),true) -OTHER_CPPFLAGS += -DMOOT_PRIORITIES -endif -LIBPOSIX4 = -lposix4 -OTHER_LDLIBS += -lpthread $(LIBPOSIX4) -endif - -HAVE_GETHRVTIME=true -ifeq ($(HAVE_GETHRVTIME),true) -OTHER_CPPFLAGS += -DHAVE_GETHRVTIME -endif - -HAVE_FILIOH=true -ifeq ($(HAVE_FILIOH),true) -OTHER_CPPFLAGS += -DHAVE_FILIOH -endif - -ifeq ($(NO_INTERRUPTIBLE_IO),true) -OTHER_CPPFLAGS += -DNO_INTERRUPTIBLE_IO -endif -
--- a/jdk/make/java/hpi/native/mapfile-vers Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -# -# Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -SUNWprivate_1.1 { - global: - DLL_Initialize; - - local: - *; -};
--- a/jdk/make/java/hpi/native/reorder-i586 Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -data = R0x2000; -text = LOAD ?RXO; -# Test Null -text: .text%_init; -text: .text%checkForCorrectLibthread: OUTPUTDIR/tmp/java/hpi/native_threads/obj/threads_solaris.o; -text: .text%init64IO: OUTPUTDIR/tmp/java/hpi/native_threads/obj/system_md.o; -text: .text%DLL_Initialize; -text: .text%GetInterface: OUTPUTDIR/tmp/java/hpi/native_threads/obj/hpi.o; -text: .text%sysBuildLibName; -text: .text%sysLoadLibrary; -text: .text%sysFindLibraryEntry; -text: .text%sysNativePath; -text: .text%sysOpen; -text: .text%sysSeek; -text: .text%lseek64_w; -# Test Exit -# Test Hello -# Test Sleep -# Test IntToString -# Test LoadToolkit -text: .text%sysAvailable; -text: .text%sysFfileMode; -text: .text%sysGetLastErrorString; -# Test LoadFrame -# Test LoadJFrame -# Test JHello -# SwingSet
--- a/jdk/make/java/hpi/native/reorder-sparc Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -data = R0x2000; -text = LOAD ?RXO; -# Test Null -text: .text%checkForCorrectLibthread: OUTPUTDIR/tmp/java/hpi/native_threads/obj/threads_solaris.o; -text: .text%init64IO: OUTPUTDIR/tmp/java/hpi/native_threads/obj/system_md.o; -text: .text%DLL_Initialize; -text: .text%GetInterface: OUTPUTDIR/tmp/java/hpi/native_threads/obj/hpi.o; -text: .text%sysBuildLibName; -text: .text%sysLoadLibrary; -text: .text%sysFindLibraryEntry; -text: .text%sysNativePath; -text: .text%sysOpen; -text: .text%sysFfileMode; -text: .text%sysSeek; -text: .text%lseek64_w; -text: .text%sysAvailable; -# Test Exit -# Test Hello -# Test Sleep -# Test IntToString -# Test LoadToolkit -text: .text%sysGetLastErrorString; -# Test LoadFrame -# Test LoadJFrame -# Test JHello -# SwingSet
--- a/jdk/make/java/hpi/native/reorder-sparcv9 Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -data = R0x2000; -text = LOAD ?RXO; -# Test Null -text: .text%checkForCorrectLibthread: OUTPUTDIR/tmp/java/hpi/native_threads/obj64/threads_solaris.o; -text: .text%init64IO: OUTPUTDIR/tmp/java/hpi/native_threads/obj64/system_md.o; -text: .text%DLL_Initialize; -text: .text%GetInterface: OUTPUTDIR/tmp/java/hpi/native_threads/obj64/hpi.o; -text: .text%sysBuildLibName; -text: .text%sysLoadLibrary; -text: .text%sysFindLibraryEntry; -text: .text%sysNativePath; -text: .text%sysOpen; -text: .text%sysFfileMode; -text: .text%sysSeek; -text: .text%lseek64_w; -text: .text%sysAvailable; -# Test Exit -# Test Hello -# Test Sleep -# Test IntToString -# Test LoadToolkit -text: .text%sysGetLastErrorString; -# Test LoadFrame -# Test LoadJFrame -# Test JHello -# SwingSet
--- a/jdk/make/java/hpi/windows/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -# -# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# Makefile for Windows HPI DLL -# -BUILDDIR = ../../.. -MODULE = base -LIBRARY = hpi -PRODUCT = java -THREADDIR = windows_threads -LIB_LOCATION = $(BINDIR) - -include $(BUILDDIR)/common/Defs.gmk - -# windows compiler flags -ifeq ($(PLATFORM),windows) - CPPFLAGS_DBG += -DLOGGING -endif - -FILES_c = \ - linker_md.c \ - memory_md.c \ - monitor_md.c \ - path_md.c \ - socket_md.c \ - sys_api_md.c \ - system_md.c \ - threads_md.c \ - hpi.c # trailing blank required! - -JVMLIB = -JAVALIB = -OTHER_LCF = -export:DLL_Initialize -EXTRA_LIBS = - - -# -# Other files/flags shared between the HPIs. -# -include $(BUILDDIR)/java/hpi/hpi_common.gmk - -# -# Rules for the .so file. -# -include $(BUILDDIR)/common/Library.gmk -
--- a/jdk/make/java/management/mapfile-vers Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/java/management/mapfile-vers Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -70,14 +70,18 @@ Java_sun_management_ThreadImpl_dumpThreads0; Java_sun_management_ThreadImpl_findDeadlockedThreads0; Java_sun_management_ThreadImpl_findMonitorDeadlockedThreads0; - Java_sun_management_ThreadImpl_getThreadInfo0; + Java_sun_management_ThreadImpl_getThreadInfo1; Java_sun_management_ThreadImpl_getThreads; Java_sun_management_ThreadImpl_getThreadTotalCpuTime0; + Java_sun_management_ThreadImpl_getThreadTotalCpuTime1; Java_sun_management_ThreadImpl_getThreadUserCpuTime0; + Java_sun_management_ThreadImpl_getThreadUserCpuTime1; + Java_sun_management_ThreadImpl_getThreadAllocatedMemory1; Java_sun_management_ThreadImpl_resetContentionTimes0; Java_sun_management_ThreadImpl_resetPeakThreadCount0; Java_sun_management_ThreadImpl_setThreadContentionMonitoringEnabled0; Java_sun_management_ThreadImpl_setThreadCpuTimeEnabled0; + Java_sun_management_ThreadImpl_setThreadAllocatedMemoryEnabled0; Java_sun_management_VMManagementImpl_getAvailableProcessors; Java_sun_management_VMManagementImpl_getClassInitializationTime; Java_sun_management_VMManagementImpl_getClassLoadingTime; @@ -106,6 +110,7 @@ Java_sun_management_VMManagementImpl_initOptionalSupportFields; Java_sun_management_VMManagementImpl_isThreadContentionMonitoringEnabled; Java_sun_management_VMManagementImpl_isThreadCpuTimeEnabled; + Java_sun_management_VMManagementImpl_isThreadAllocatedMemoryEnabled; JNI_OnLoad; local: *;
--- a/jdk/make/java/nio/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/java/nio/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -296,7 +296,7 @@ OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl endif ifeq ($(PLATFORM), solaris) -OTHER_LDLIBS += $(JVMLIB) $(LIBSOCKET) -lposix4 -ldl \ +OTHER_LDLIBS += $(JVMLIB) $(LIBSOCKET) -lposix4 -ldl -lsendfile \ -L$(LIBDIR)/$(LIBARCH) -ljava -lnet endif # PLATFORM
--- a/jdk/make/mkdemo/jfc/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/mkdemo/jfc/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -43,7 +43,7 @@ # Some demos aren't currently included in OpenJDK ifndef OPENJDK - SUBDIRS += Java2D SwingSet2 Stylepad + SUBDIRS += Java2D SwingSet2 SwingSet3 Stylepad endif include $(BUILDDIR)/common/Subdirs.gmk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/mkdemo/jfc/SwingSet3/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -0,0 +1,43 @@ +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile to build the SwingSet3 demo. +# + +BUILDDIR = ../../.. +PRODUCT = demo/jfc +DEMONAME = SwingSet3 +include $(BUILDDIR)/common/Defs.gmk + +DEMO_ROOT = $(CLOSED_SRC)/share/demo/jfc/$(DEMONAME) +DEMO_DESTDIR = $(DEMODIR)/jfc/$(DEMONAME) +DEMO_TOPFILES = ./readme.html ./swingset3.png +DEMO_SKIP_SRCZIP = true + +# +# Demo jar building rules. +# +include $(BUILDDIR)/common/Demo.gmk
--- a/jdk/make/sun/awt/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/awt/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -369,10 +369,6 @@ _FONTCONFIGS = \ fontconfig.properties \ fontconfig.RedHat.properties \ - fontconfig.RedHat.2.1.properties \ - fontconfig.RedHat.3.properties \ - fontconfig.RedHat.4.properties \ - fontconfig.Sun.properties \ fontconfig.Turbo.properties \ fontconfig.SuSE.10.properties \ fontconfig.SuSE.11.properties @@ -388,9 +384,7 @@ FONTCONFIGS_SRC = $(PLATFORM_SRC)/classes/sun/awt/fontconfigs _FONTCONFIGS = \ - fontconfig.properties \ - fontconfig.5.9.properties \ - fontconfig.5.8.properties + fontconfig.properties FONTCONFIGS_SRC_PREFIX = $(PLATFORM).
--- a/jdk/make/sun/awt/mapfile-mawt-vers Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/awt/mapfile-mawt-vers Wed Feb 09 09:32:04 2011 -0500 @@ -515,8 +515,7 @@ getDefaultConfig; Java_sun_font_FontConfigManager_getFontConfig; Java_sun_font_FontConfigManager_getFontConfigAASettings; - Java_sun_awt_X11FontManager_getFontPath; - Java_sun_awt_X11FontManager_setNativeFontPath; + Java_sun_awt_X11FontManager_getFontPathNative; Java_sun_font_SunFontManager_populateFontFileNameMap; # CDE private entry point
--- a/jdk/make/sun/awt/mapfile-vers-linux Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/awt/mapfile-vers-linux Wed Feb 09 09:32:04 2011 -0500 @@ -537,8 +537,7 @@ getDefaultConfig; Java_sun_font_FontConfigManager_getFontConfig; Java_sun_font_FontConfigManager_getFontConfigAASettings; - Java_sun_awt_X11FontManager_getFontPath; - Java_sun_awt_X11FontManager_setNativeFontPath; + Java_sun_awt_X11FontManager_getFontPathNative; Java_sun_font_SunFontManager_populateFontFileNameMap; # CDE private entry point
--- a/jdk/make/sun/headless/mapfile-vers Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/headless/mapfile-vers Wed Feb 09 09:32:04 2011 -0500 @@ -65,7 +65,7 @@ Java_sun_font_FontConfigManager_getFontConfig; Java_sun_font_FontConfigManager_getFontConfigAASettings; Java_sun_font_FontConfigManager_getFontConfigVersion; - Java_sun_awt_X11FontManager_getFontPath; + Java_sun_awt_X11FontManager_getFontPathNative; Java_sun_awt_FontDescriptor_initIDs; Java_sun_awt_PlatformFont_initIDs;
--- a/jdk/make/sun/net/FILES_java.gmk Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/net/FILES_java.gmk Wed Feb 09 09:32:04 2011 -0500 @@ -33,6 +33,7 @@ sun/net/ProgressEvent.java \ sun/net/ProgressListener.java \ sun/net/ProgressMeteringPolicy.java \ + sun/net/SocksProxy.java \ sun/net/TelnetInputStream.java \ sun/net/TelnetOutputStream.java \ sun/net/TelnetProtocolException.java \
--- a/jdk/make/sun/xawt/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/xawt/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -136,8 +136,25 @@ -I$(OPENWIN_HOME)/include endif +# We have some odd logic here because some Solaris 10 updates +# have a render.h file that suggests gradients are supported, but +# the Xrender.h doesn't have the corresponding type definitions. +# Earlier updates have neither. We'd like to know if there's a mismatch. +# Whilst in the C preprocessor we can tell if the render.h define's are set +# we can't tell anything about C declarations. +# A grep of Xrender.h is the only way to know this. If they are absent +# we will set a flag indicating this mismatch and the JDK source file +# will interpret it to resolve the problem. ifeq ($(PLATFORM), solaris) CPPFLAGS += -I$(OPENWIN_HOME)/include/X11/extensions + OS_VERSION := $(shell uname -r) + XRENDER_H := $(OPENWIN_HOME)/share/include/X11/extensions/Xrender.h + ifeq ($(OS_VERSION),5.10) + LINEARGRADIENT_CNT := $(shell $(EGREP) -c XLinearGradient $(XRENDER_H)) + ifeq ($(LINEARGRADIENT_CNT),0) + CFLAGS+= -DSOLARIS10_NO_XRENDER_STRUCTS + endif + endif endif ifeq ($(MILESTONE), internal)
--- a/jdk/make/sun/xawt/mapfile-vers Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/sun/xawt/mapfile-vers Wed Feb 09 09:32:04 2011 -0500 @@ -188,8 +188,7 @@ Java_sun_font_FontConfigManager_getFontConfig; Java_sun_font_FontConfigManager_getFontConfigAASettings; Java_sun_font_FontConfigManager_getFontConfigVersion; - Java_sun_awt_X11FontManager_getFontPath; - Java_sun_font_X11FontManager_setNativeFontPath; + Java_sun_awt_X11FontManager_getFontPathNative; Java_sun_awt_X11GraphicsEnvironment_initDisplay; Java_sun_awt_X11GraphicsEnvironment_initGLX; Java_sun_awt_X11GraphicsEnvironment_initXRender;
--- a/jdk/make/tools/reorder/Makefile Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/make/tools/reorder/Makefile Wed Feb 09 09:32:04 2011 -0500 @@ -85,7 +85,6 @@ libs.reorder : ifeq ($(PLATFORM), solaris) $(MAKE) LIBBLDDIR=java/zip LIBTMPDIR=sun/java.util.zip/zip reorder.lib - $(MAKE) LIBBLDDIR=java/hpi/native LIBTMPDIR=java/hpi/native_threads reorder.lib $(MAKE) LIBBLDDIR=java/java LIBTMPDIR=java/java.lang/java reorder.lib $(MAKE) LIBBLDDIR=java/nio LIBTMPDIR=java/java.nio/nio reorder.lib $(MAKE) LIBBLDDIR=sun/font LIBTMPDIR=sun/sun.awt.font/fontmanager reorder.lib @@ -96,7 +95,6 @@ libs.copy: ifeq ($(PLATFORM), solaris) $(CP) $(OUTDIR)/reorder_java_zip-$(ARCH) ../../java/zip/reorder-$(ARCH) - $(CP) $(OUTDIR)/reorder_java_hpi_native-$(ARCH) ../../java/hpi/native/reorder-$(ARCH) $(CP) $(OUTDIR)/reorder_java_java-$(ARCH) ../../java/java/reorder-$(ARCH) $(CP) $(OUTDIR)/reorder_sun_font-$(ARCH) ../../sun/font/reorder-$(ARCH) $(CP) $(OUTDIR)/reorder_sun_jpeg-$(ARCH) ../../sun/jpeg/reorder-$(ARCH)
--- a/jdk/src/share/back/debugInit.c Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/back/debugInit.c Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,27 +133,60 @@ return error; } +typedef struct { + int major; + int minor; +} version_type; + +typedef struct { + version_type runtime; + version_type compiletime; +} compatible_versions_type; + +/* + * List of explicitly compatible JVMTI versions, specified as + * { runtime version, compile-time version } pairs. -1 is a wildcard. + */ +static int nof_compatible_versions = 3; +static compatible_versions_type compatible_versions_list[] = { + /* + * FIXUP: Allow version 0 to be compatible with anything + * Special check for FCS of 1.0. + */ + { { 0, -1 }, { -1, -1 } }, + { { -1, -1 }, { 0, -1 } }, + /* + * 1.2 is runtime compatible with 1.1 -- just make sure to check the + * version before using any new 1.2 features + */ + { { 1, 1 }, { 1, 2 } } +}; + + /* Logic to determine JVMTI version compatibility */ static jboolean compatible_versions(jint major_runtime, jint minor_runtime, jint major_compiletime, jint minor_compiletime) { -#if 1 /* FIXUP: We allow version 0 to be compatible with anything */ - /* Special check for FCS of 1.0. */ - if ( major_runtime == 0 || major_compiletime == 0 ) { - return JNI_TRUE; + /* + * First check to see if versions are explicitly compatible via the + * list specified above. + */ + int i; + for (i = 0; i < nof_compatible_versions; ++i) { + version_type runtime = compatible_versions_list[i].runtime; + version_type comptime = compatible_versions_list[i].compiletime; + + if ((major_runtime == runtime.major || runtime.major == -1) && + (minor_runtime == runtime.minor || runtime.minor == -1) && + (major_compiletime == comptime.major || comptime.major == -1) && + (minor_compiletime == comptime.minor || comptime.minor == -1)) { + return JNI_TRUE; + } } -#endif - /* Runtime major version must match. */ - if ( major_runtime != major_compiletime ) { - return JNI_FALSE; - } - /* Runtime minor version must be >= the version compiled with. */ - if ( minor_runtime < minor_compiletime ) { - return JNI_FALSE; - } - /* Assumed compatible */ - return JNI_TRUE; + + return major_runtime == major_compiletime && + minor_runtime >= minor_compiletime; } /* OnLoad startup:
--- a/jdk/src/share/back/eventFilter.c Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/back/eventFilter.c Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ #include "stepControl.h" #include "threadControl.h" #include "SDE.h" +#include "jvmti.h" typedef struct ClassFilter { jclass clazz; @@ -275,6 +276,24 @@ } } +static jboolean isVersionGte12x() { + jint version; + jvmtiError err = + JVMTI_FUNC_PTR(gdata->jvmti,GetVersionNumber)(gdata->jvmti, &version); + + if (err == JVMTI_ERROR_NONE) { + jint major, minor; + + major = (version & JVMTI_VERSION_MASK_MAJOR) + >> JVMTI_VERSION_SHIFT_MAJOR; + minor = (version & JVMTI_VERSION_MASK_MINOR) + >> JVMTI_VERSION_SHIFT_MINOR; + return (major > 1 || major == 1 && minor >= 2); + } else { + return JNI_FALSE; + } +} + /* Return the object instance in which the event occurred */ /* Return NULL if static or if an error occurs */ static jobject @@ -286,6 +305,14 @@ jint modifiers = 0; jvmtiError error; + static jboolean got_version = JNI_FALSE; + static jboolean is_version_gte_12x = JNI_FALSE; + + if (!got_version) { + is_version_gte_12x = isVersionGte12x(); + got_version = JNI_TRUE; + } + switch (evinfo->ei) { case EI_SINGLE_STEP: case EI_BREAKPOINT: @@ -314,11 +341,18 @@ /* fail if error or static (0x8) */ if (error == JVMTI_ERROR_NONE && thread!=NULL && (modifiers & 0x8) == 0) { FrameNumber fnum = 0; - /* get slot zero object "this" */ - error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject) - (gdata->jvmti, thread, fnum, 0, &object); - if (error != JVMTI_ERROR_NONE) + if (is_version_gte_12x) { + /* Use new 1.2.x function, GetLocalInstance */ + error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalInstance) + (gdata->jvmti, thread, fnum, &object); + } else { + /* get slot zero object "this" */ + error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject) + (gdata->jvmti, thread, fnum, 0, &object); + } + if (error != JVMTI_ERROR_NONE) { object = NULL; + } } return object;
--- a/jdk/src/share/bin/java.c Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/bin/java.c Wed Feb 09 09:32:04 2011 -0500 @@ -94,15 +94,15 @@ * Prototypes for functions internal to launcher. */ static void SetClassPath(const char *s); -static void SetModulesBootClassPath(const char *s); static void SelectVersion(int argc, char **argv, char **main_class); -static jboolean ParseArguments(int *pargc, char ***pargv, char **pjarfile, - char **pclassname, int *pret, const char *jvmpath); +static jboolean ParseArguments(int *pargc, char ***pargv, + int *pmode, char **pwhat, + int *pret, const char *jrepath); static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn); static jstring NewPlatformString(JNIEnv *env, char *s); static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc); -static jclass LoadMainClass(JNIEnv *env, jboolean isJar, char *name); +static jclass LoadMainClass(JNIEnv *env, int mode, char *name); static void TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, char ***pargv); static jboolean AddApplicationOptions(int cpathc, const char **cpathv); @@ -158,18 +158,27 @@ * Running Java code in primordial thread caused many problems. We will * create a new thread to invoke JVM. See 6316197 for more information. */ -static jlong threadStackSize = 0; /* stack size of the new thread */ +static jlong threadStackSize = 0; /* stack size of the new thread */ static jlong maxHeapSize = 0; /* max heap size */ static jlong initialHeapSize = 0; /* inital heap size */ int JNICALL JavaMain(void * args); /* entry point */ +enum LaunchMode { // cf. sun.launcher.LauncherHelper + LM_UNKNOWN = 0, + LM_CLASS, + LM_JAR +}; + +static const char *launchModeNames[] + = { "Unknown", "Main class", "JAR file" }; + typedef struct { - int argc; - char ** argv; - char * jarfile; - char * classname; - InvocationFunctions ifn; + int argc; + char **argv; + int mode; + char *what; + InvocationFunctions ifn; } JavaMainArgs; /* @@ -189,8 +198,8 @@ jint ergo /* ergonomics class policy */ ) { - char *jarfile = 0; - char *classname = 0; + int mode = LM_UNKNOWN; + char *what = NULL; char *cpath = 0; char *main_class = NULL; int ret; @@ -277,24 +286,21 @@ SetClassPath(cpath); } - /* - * Parse command line options; if the return value of - * ParseArguments is false, the program should exit. + /* Parse command line options; if the return value of + * ParseArguments is false, the program should exit. */ - if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret, jvmpath)) { + if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)) + { return(ret); } - /* Set bootclasspath for modules */ - SetModulesBootClassPath(jrepath); - /* Override class path if -jar flag was specified */ - if (jarfile != 0) { - SetClassPath(jarfile); + if (mode == LM_JAR) { + SetClassPath(what); /* Override class path */ } /* set the -Dsun.java.command pseudo property */ - SetJavaCommandLineProp(classname, jarfile, argc, argv); + SetJavaCommandLineProp(what, argc, argv); /* Set the -Dsun.java.launcher pseudo property */ SetJavaLauncherProp(); @@ -305,7 +311,7 @@ /* Show the splash screen if needed */ ShowSplashScreen(); - return ContinueInNewThread(&ifn, argc, argv, jarfile, classname, ret); + return ContinueInNewThread(&ifn, argc, argv, mode, what, ret); } /* @@ -353,13 +359,13 @@ JavaMainArgs *args = (JavaMainArgs *)_args; int argc = args->argc; char **argv = args->argv; - char *jarfile = args->jarfile; - char *classname = args->classname; + int mode = args->mode; + char *what = args->what; InvocationFunctions ifn = args->ifn; JavaVM *vm = 0; JNIEnv *env = 0; - jclass mainClass; + jclass mainClass = NULL; jmethodID mainID; jobjectArray mainArgs; int ret = 0; @@ -385,7 +391,7 @@ CHECK_EXCEPTION_LEAVE(1); } /* If the user specified neither a class name nor a JAR file */ - if (printXUsage || printUsage || (jarfile == 0 && classname == 0)) { + if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) { PrintUsage(env, printXUsage); CHECK_EXCEPTION_LEAVE(1); LEAVE(); @@ -399,11 +405,11 @@ (long)(jint)Counter2Micros(end-start)); } - /* At this stage, argc/argv have the applications' arguments */ + /* At this stage, argc/argv have the application's arguments */ if (JLI_IsTraceLauncher()){ int i; - printf("Main-Class is '%s'\n", classname ? classname : ""); - printf("Apps' argc is %d\n", argc); + printf("%s is '%s'\n", launchModeNames[mode], what); + printf("App's argc is %d\n", argc); for (i=0; i < argc; i++) { printf(" argv[%2d] = '%s'\n", i, argv[i]); } @@ -431,11 +437,7 @@ * 2) Remove the vestages of maintaining main_class through * the environment (and remove these comments). */ - if (jarfile != 0) { - mainClass = LoadMainClass(env, JNI_TRUE, jarfile); - } else { - mainClass = LoadMainClass(env, JNI_FALSE, classname); - } + mainClass = LoadMainClass(env, mode, what); CHECK_EXCEPTION_NULL_LEAVE(mainClass); /* @@ -697,7 +699,7 @@ if (JLI_StrCCmp(str, "-Xms") == 0) { jlong tmp; if (parse_size(str + 4, &tmp)) { - initialHeapSize = tmp; + initialHeapSize = tmp; } } } @@ -719,44 +721,6 @@ } /* - * Set the bootclasspath for modules. - * A temporary workaround until jigsaw is integrated into JDK 7. - */ -static void -SetModulesBootClassPath(const char *jrepath) -{ - char *def, *s; - char pathname[MAXPATHLEN]; - const char separator[] = { FILE_SEPARATOR, '\0' }; - const char *orig = jrepath; - static const char format[] = "-Xbootclasspath/p:%s"; - struct stat statbuf; - - /* return if jre/lib/rt.jar exists */ - JLI_Snprintf(pathname, sizeof(pathname), "%s%slib%srt.jar", jrepath, separator, separator); - if (stat(pathname, &statbuf) == 0) { - return; - } - - /* return if jre/classes exists */ - JLI_Snprintf(pathname, sizeof(pathname), "%s%sclasses", jrepath, separator); - if (stat(pathname, &statbuf) == 0) { - return; - } - - /* modularized jre */ - JLI_Snprintf(pathname, sizeof(pathname), "%s%slib%s*", jrepath, separator, separator); - s = (char *) JLI_WildcardExpandClasspath(pathname); - def = JLI_MemAlloc(sizeof(format) - - 2 /* strlen("%s") */ - + JLI_StrLen(s)); - sprintf(def, format, s); - AddOption(def, NULL); - if (s != orig) - JLI_MemFree((char *) s); -} - -/* * The SelectVersion() routine ensures that an appropriate version of * the JRE is running. The specification for the appropriate version * is obtained from either the manifest of a jar file (preferred) or @@ -1000,16 +964,17 @@ /* * Parses command line arguments. Returns JNI_FALSE if launcher * should exit without starting vm, returns JNI_TRUE if vm needs - * to be started to process given options. *pret (the launcher + * to be started to process given options. *pret (the launcher * process return value) is set to 0 for a normal exit. */ static jboolean -ParseArguments(int *pargc, char ***pargv, char **pjarfile, - char **pclassname, int *pret, const char *jvmpath) +ParseArguments(int *pargc, char ***pargv, + int *pmode, char **pwhat, + int *pret, const char *jrepath) { int argc = *pargc; char **argv = *pargv; - jboolean jarflag = JNI_FALSE; + int mode = LM_UNKNOWN; char *arg; *pret = 0; @@ -1019,10 +984,11 @@ if (JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) { ARG_CHECK (argc, ARG_ERROR1, arg); SetClassPath(*argv); + mode = LM_CLASS; argv++; --argc; } else if (JLI_StrCmp(arg, "-jar") == 0) { ARG_CHECK (argc, ARG_ERROR2, arg); - jarflag = JNI_TRUE; + mode = LM_JAR; } else if (JLI_StrCmp(arg, "-help") == 0 || JLI_StrCmp(arg, "-h") == 0 || JLI_StrCmp(arg, "-?") == 0) { @@ -1102,19 +1068,24 @@ } if (--argc >= 0) { - if (jarflag) { - *pjarfile = *argv++; - *pclassname = NULL; - } else { - *pjarfile = NULL; - *pclassname = *argv++; - } + *pwhat = *argv++; + } + + if (*pwhat == NULL) { + *pret = 1; + } else if (mode == LM_UNKNOWN) { + /* default to LM_CLASS if -jar and -cp option are + * not specified */ + mode = LM_CLASS; + } + + if (argc >= 0) { *pargc = argc; *pargv = argv; } - if (*pjarfile == NULL && *pclassname == NULL) { - *pret = 1; - } + + *pmode = mode; + return JNI_TRUE; } @@ -1263,7 +1234,7 @@ * call it for more details refer to the java implementation. */ static jclass -LoadMainClass(JNIEnv *env, jboolean isJar, char *name) +LoadMainClass(JNIEnv *env, int mode, char *name) { jclass cls; jmethodID mid; @@ -1276,9 +1247,9 @@ } NULL_CHECK0(cls = FindBootStrapClass(env, "sun/launcher/LauncherHelper")); NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls, "checkAndLoadMain", - "(ZZLjava/lang/String;)Ljava/lang/Object;")); + "(ZILjava/lang/String;)Ljava/lang/Class;")); str = (*env)->NewStringUTF(env, name); - result = (*env)->CallStaticObjectMethod(env, cls, mid, JNI_TRUE, isJar, str); + result = (*env)->CallStaticObjectMethod(env, cls, mid, JNI_TRUE, mode, str); if (JLI_IsTraceLauncher()) { end = CounterGet(); @@ -1424,8 +1395,7 @@ * property is not exported by HotSpot to the Java layer. */ void -SetJavaCommandLineProp(char *classname, char *jarfile, - int argc, char **argv) +SetJavaCommandLineProp(char *what, int argc, char **argv) { int i = 0; @@ -1433,22 +1403,17 @@ char* javaCommand = NULL; char* dashDstr = "-Dsun.java.command="; - if (classname == NULL && jarfile == NULL) { + if (what == NULL) { /* unexpected, one of these should be set. just return without * setting the property */ return; } - /* if the class name is not set, then use the jarfile name */ - if (classname == NULL) { - classname = jarfile; - } - /* determine the amount of memory to allocate assuming * the individual components will be space separated */ - len = JLI_StrLen(classname); + len = JLI_StrLen(what); for (i = 0; i < argc; i++) { len += JLI_StrLen(argv[i]) + 1; } @@ -1459,7 +1424,7 @@ /* build the -D string */ *javaCommand = '\0'; JLI_StrCat(javaCommand, dashDstr); - JLI_StrCat(javaCommand, classname); + JLI_StrCat(javaCommand, what); for (i = 0; i < argc; i++) { /* the components of the string are space separated. In @@ -1479,7 +1444,8 @@ * JVM would like to know if it's created by a standard Sun launcher, or by * user native application, the following property indicates the former. */ -void SetJavaLauncherProp() { +void +SetJavaLauncherProp() { AddOption("-Dsun.java.launcher=SUN_STANDARD", NULL); } @@ -1913,8 +1879,8 @@ } static int -ContinueInNewThread(InvocationFunctions* ifn, int argc, - char **argv, char *jarfile, char *classname, int ret) +ContinueInNewThread(InvocationFunctions* ifn, int argc, char **argv, + int mode, char *what, int ret) { /* @@ -1938,8 +1904,8 @@ args.argc = argc; args.argv = argv; - args.jarfile = jarfile; - args.classname = classname; + args.mode = mode; + args.what = what; args.ifn = *ifn; rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
--- a/jdk/src/share/bin/java.h Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/bin/java.h Wed Feb 09 09:32:04 2011 -0500 @@ -153,7 +153,7 @@ /* sun.java.launcher.* platform properties. */ void SetJavaLauncherPlatformProps(void); -void SetJavaCommandLineProp(char* classname, char* jarfile, int argc, char** argv); +void SetJavaCommandLineProp(char* what, int argc, char** argv); void SetJavaLauncherProp(void); /* @@ -178,8 +178,9 @@ jboolean ServerClassMachine(); -static int ContinueInNewThread(InvocationFunctions* ifn, int argc, char** argv, - char* jarfile, char* classname, int ret); +static int ContinueInNewThread(InvocationFunctions* ifn, + int argc, char** argv, + int mode, char *what, int ret); /* * Initialize platform specific settings
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java Wed Feb 09 09:32:04 2011 -0500 @@ -1704,7 +1704,7 @@ for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { assert(attrIndexLimit[i] == 0); attrIndexLimit[i] = 32; // just for the sake of predefs. - attrDefs.set(i, new ArrayList<>(Collections.nCopies( + attrDefs.set(i, new ArrayList<Attribute.Layout>(Collections.nCopies( attrIndexLimit[i], (Attribute.Layout)null))); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/com/sun/management/ThreadMXBean.java Wed Feb 09 09:32:04 2011 -0500 @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.management; + +import java.util.Map; + +/** + * Platform-specific management interface for the thread system + * of the Java virtual machine. + * <p> + * This platform extension is only available to a thread + * implementation that supports this extension. + * + * @author Paul Hohensee + * @since 6u25 + */ + +public interface ThreadMXBean extends java.lang.management.ThreadMXBean { + /** + * Returns the total CPU time for each thread whose ID is + * in the input array {@code ids} in nanoseconds. + * The returned values are of nanoseconds precision but + * not necessarily nanoseconds accuracy. + * <p> + * This method is equivalent to calling the + * {@link ThreadMXBean#getThreadCpuTime(long)} + * method for each thread ID in the input array {@code ids} and setting the + * returned value in the corresponding element of the returned array. + * + * @param ids an array of thread IDs. + * @return an array of long values, each of which is the amount of CPU + * time the thread whose ID is in the corresponding element of the input + * array of IDs has used, + * if the thread of a specified ID exists, the thread is alive, + * and CPU time measurement is enabled; + * {@code -1} otherwise. + * + * @throws NullPointerException if {@code ids} is {@code null} + * @throws IllegalArgumentException if any element in the input array + * {@code ids} is {@code <=} {@code 0}. + * @throws java.lang.UnsupportedOperationException if the Java + * virtual machine implementation does not support CPU time + * measurement. + * + * @see ThreadMXBean#getThreadCpuTime(long) + * @see #getThreadUserTime + * @see ThreadMXBean#isThreadCpuTimeSupported + * @see ThreadMXBean#isThreadCpuTimeEnabled + * @see ThreadMXBean#setThreadCpuTimeEnabled + */ + public long[] getThreadCpuTime(long[] ids); + + /** + * Returns the CPU time that each thread whose ID is in the input array + * {@code ids} has executed in user mode in nanoseconds. + * The returned values are of nanoseconds precision but + * not necessarily nanoseconds accuracy. + * <p> + * This method is equivalent to calling the + * {@link ThreadMXBean#getThreadUserTime(long)} + * method for each thread ID in the input array {@code ids} and setting the + * returned value in the corresponding element of the returned array. + * + * @param ids an array of thread IDs. + * @return an array of long values, each of which is the amount of user + * mode CPU time the thread whose ID is in the corresponding element of + * the input array of IDs has used, + * if the thread of a specified ID exists, the thread is alive, + * and CPU time measurement is enabled; + * {@code -1} otherwise. + * + * @throws NullPointerException if {@code ids} is {@code null} + * @throws IllegalArgumentException if any element in the input array + * {@code ids} is {@code <=} {@code 0}. + * @throws java.lang.UnsupportedOperationException if the Java + * virtual machine implementation does not support CPU time + * measurement. + * + * @see ThreadMXBean#getThreadUserTime(long) + * @see #getThreadCpuTime + * @see ThreadMXBean#isThreadCpuTimeSupported + * @see ThreadMXBean#isThreadCpuTimeEnabled + * @see ThreadMXBean#setThreadCpuTimeEnabled + */ + public long[] getThreadUserTime(long[] ids); + + /** + * Returns an approximation of the total amount of memory, in bytes, + * allocated in heap memory for the thread of the specified ID. + * The returned value is an approximation because some Java virtual machine + * implementations may use object allocation mechanisms that result in a + * delay between the time an object is allocated and the time its size is + * recorded. + * <p> + * If the thread of the specified ID is not alive or does not exist, + * this method returns {@code -1}. If thread memory allocation measurement + * is disabled, this method returns {@code -1}. + * A thread is alive if it has been started and has not yet died. + * <p> + * If thread memory allocation measurement is enabled after the thread has + * started, the Java virtual machine implementation may choose any time up + * to and including the time that the capability is enabled as the point + * where thread memory allocation measurement starts. + * + * @param id the thread ID of a thread + * @return an approximation of the total memory allocated, in bytes, in + * heap memory for a thread of the specified ID + * if the thread of the specified ID exists, the thread is alive, + * and thread memory allocation measurement is enabled; + * {@code -1} otherwise. + * + * @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}. + * @throws java.lang.UnsupportedOperationException if the Java virtual + * machine implementation does not support thread memory allocation + * measurement. + * + * @see #isThreadAllocatedMemorySupported + * @see #isThreadAllocatedMemoryEnabled + * @see #setThreadAllocatedMemoryEnabled + */ + public long getThreadAllocatedBytes(long id); + + /** + * Returns an approximation of the total amount of memory, in bytes, + * allocated in heap memory for each thread whose ID is in the input + * array {@code ids}. + * The returned values are approximations because some Java virtual machine + * implementations may use object allocation mechanisms that result in a + * delay between the time an object is allocated and the time its size is + * recorded. + * <p> + * This method is equivalent to calling the + * {@link #getThreadAllocatedBytes(long)} + * method for each thread ID in the input array {@code ids} and setting the + * returned value in the corresponding element of the returned array. + * + * @param ids an array of thread IDs. + * @return an array of long values, each of which is an approximation of + * the total memory allocated, in bytes, in heap memory for the thread + * whose ID is in the corresponding element of the input array of IDs. + * + * @throws NullPointerException if {@code ids} is {@code null} + * @throws IllegalArgumentException if any element in the input array + * {@code ids} is {@code <=} {@code 0}. + * @throws java.lang.UnsupportedOperationException if the Java virtual + * machine implementation does not support thread memory allocation + * measurement. + * + * @see #getThreadAllocatedBytes(long) + * @see #isThreadAllocatedMemorySupported + * @see #isThreadAllocatedMemoryEnabled + * @see #setThreadAllocatedMemoryEnabled + */ + public long[] getThreadAllocatedBytes(long[] ids); + + /** + * Tests if the Java virtual machine implementation supports thread memory + * allocation measurement. + * + * @return + * {@code true} + * if the Java virtual machine implementation supports thread memory + * allocation measurement; + * {@code false} otherwise. + */ + public boolean isThreadAllocatedMemorySupported(); + + /** + * Tests if thread memory allocation measurement is enabled. + * + * @return {@code true} if thread memory allocation measurement is enabled; + * {@code false} otherwise. + * + * @throws java.lang.UnsupportedOperationException if the Java virtual + * machine does not support thread memory allocation measurement. + * + * @see #isThreadAllocatedMemorySupported + */ + public boolean isThreadAllocatedMemoryEnabled(); + + /** + * Enables or disables thread memory allocation measurement. The default + * is platform dependent. + * + * @param enable {@code true} to enable; + * {@code false} to disable. + * + * @throws java.lang.UnsupportedOperationException if the Java virtual + * machine does not support thread memory allocation measurement. + * + * @throws java.lang.SecurityException if a security manager + * exists and the caller does not have + * ManagementPermission("control"). + * + * @see #isThreadAllocatedMemorySupported + */ + public void setThreadAllocatedMemoryEnabled(boolean enable); +}
--- a/jdk/src/share/classes/com/sun/script/javascript/RhinoScriptEngine.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/script/javascript/RhinoScriptEngine.java Wed Feb 09 09:32:04 2011 -0500 @@ -223,7 +223,9 @@ } catch (RhinoException re) { if (DEBUG) re.printStackTrace(); int line = (line = re.lineNumber()) == 0 ? -1 : line; - throw new ScriptException(re.toString(), re.sourceName(), line); + ScriptException se = new ScriptException(re.toString(), re.sourceName(), line); + se.initCause(re); + throw se; } finally { cx.exit(); } @@ -257,6 +259,8 @@ " str = 'null'; \n" + " } \n" + " var out = context.getWriter(); \n" + + " if (!(out instanceof java.io.PrintWriter))\n" + + " out = new java.io.PrintWriter(out); \n" + " out.print(String(str)); \n" + " if (newline) out.print('\\n'); \n" + " out.flush(); \n" +
--- a/jdk/src/share/classes/com/sun/security/auth/PolicyFile.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/PolicyFile.java Wed Feb 09 09:32:04 2011 -0500 @@ -1180,7 +1180,7 @@ // Done return certs; - ArrayList<Certificate> userCertList = new ArrayList<Certificate>(); + ArrayList<Certificate> userCertList = new ArrayList<>(); i = 0; while (i < certs.length) { userCertList.add(certs[i]);
--- a/jdk/src/share/classes/com/sun/security/auth/callback/DialogCallbackHandler.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/callback/DialogCallbackHandler.java Wed Feb 09 09:32:04 2011 -0500 @@ -99,10 +99,10 @@ throws UnsupportedCallbackException { /* Collect messages to display in the dialog */ - final List<Object> messages = new ArrayList<Object>(3); + final List<Object> messages = new ArrayList<>(3); /* Collection actions to perform if the user clicks OK */ - final List<Action> okActions = new ArrayList<Action>(2); + final List<Action> okActions = new ArrayList<>(2); ConfirmationInfo confirmation = new ConfirmationInfo();
--- a/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Wed Feb 09 09:32:04 2011 -0500 @@ -152,7 +152,7 @@ // new configuration HashMap<String, LinkedList<AppConfigurationEntry>> newConfig = - new HashMap<String, LinkedList<AppConfigurationEntry>>(); + new HashMap<>(); if (url != null) { @@ -392,8 +392,7 @@ String moduleClass; String sflag; AppConfigurationEntry.LoginModuleControlFlag controlFlag; - LinkedList<AppConfigurationEntry> configEntries = - new LinkedList<AppConfigurationEntry>(); + LinkedList<AppConfigurationEntry> configEntries = new LinkedList<>(); // application name appName = st.sval; @@ -433,7 +432,7 @@ } // get the args - HashMap<String, String> options = new HashMap<String, String>(); + HashMap<String, String> options = new HashMap<>(); String key; String value; while (peek(";") == false) {
--- a/jdk/src/share/classes/com/sun/security/auth/module/JndiLoginModule.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/module/JndiLoginModule.java Wed Feb 09 09:32:04 2011 -0500 @@ -184,7 +184,7 @@ private UnixNumericUserPrincipal UIDPrincipal; private UnixNumericGroupPrincipal GIDPrincipal; private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups = - new LinkedList<UnixNumericGroupPrincipal>(); + new LinkedList<>(); // initial state private Subject subject;
--- a/jdk/src/share/classes/com/sun/security/auth/module/KeyStoreLoginModule.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/module/KeyStoreLoginModule.java Wed Feb 09 09:32:04 2011 -0500 @@ -658,8 +658,7 @@ throw new FailedLoginException( "Unable to find X.509 certificate chain in keystore"); } else { - LinkedList<Certificate> certList = - new LinkedList<Certificate>(); + LinkedList<Certificate> certList = new LinkedList<>(); for (int i=0; i < fromKeyStore.length; i++) { certList.add(fromKeyStore[i]); }
--- a/jdk/src/share/classes/com/sun/security/auth/module/SolarisLoginModule.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/module/SolarisLoginModule.java Wed Feb 09 09:32:04 2011 -0500 @@ -76,7 +76,7 @@ private SolarisNumericUserPrincipal UIDPrincipal; private SolarisNumericGroupPrincipal GIDPrincipal; private LinkedList<SolarisNumericGroupPrincipal> supplementaryGroups = - new LinkedList<SolarisNumericGroupPrincipal>(); + new LinkedList<>(); /** * Initialize this <code>LoginModule</code>.
--- a/jdk/src/share/classes/com/sun/security/auth/module/UnixLoginModule.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/com/sun/security/auth/module/UnixLoginModule.java Wed Feb 09 09:32:04 2011 -0500 @@ -70,7 +70,7 @@ private UnixNumericUserPrincipal UIDPrincipal; private UnixNumericGroupPrincipal GIDPrincipal; private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups = - new LinkedList<UnixNumericGroupPrincipal>(); + new LinkedList<>(); /** * Initialize this <code>LoginModule</code>.
--- a/jdk/src/share/classes/java/awt/geom/CubicCurve2D.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/awt/geom/CubicCurve2D.java Wed Feb 09 09:32:04 2011 -0500 @@ -1387,203 +1387,13 @@ return false; } - // Trivially accept if either endpoint is inside the rectangle - // (not on its border since it may end there and not go inside) - // Record where they lie with respect to the rectangle. - // -1 => left, 0 => inside, 1 => right - double x1 = getX1(); - double y1 = getY1(); - int x1tag = getTag(x1, x, x+w); - int y1tag = getTag(y1, y, y+h); - if (x1tag == INSIDE && y1tag == INSIDE) { - return true; - } - double x2 = getX2(); - double y2 = getY2(); - int x2tag = getTag(x2, x, x+w); - int y2tag = getTag(y2, y, y+h); - if (x2tag == INSIDE && y2tag == INSIDE) { - return true; - } - - double ctrlx1 = getCtrlX1(); - double ctrly1 = getCtrlY1(); - double ctrlx2 = getCtrlX2(); - double ctrly2 = getCtrlY2(); - int ctrlx1tag = getTag(ctrlx1, x, x+w); - int ctrly1tag = getTag(ctrly1, y, y+h); - int ctrlx2tag = getTag(ctrlx2, x, x+w); - int ctrly2tag = getTag(ctrly2, y, y+h); - - // Trivially reject if all points are entirely to one side of - // the rectangle. - if (x1tag < INSIDE && x2tag < INSIDE && - ctrlx1tag < INSIDE && ctrlx2tag < INSIDE) - { - return false; // All points left - } - if (y1tag < INSIDE && y2tag < INSIDE && - ctrly1tag < INSIDE && ctrly2tag < INSIDE) - { - return false; // All points above - } - if (x1tag > INSIDE && x2tag > INSIDE && - ctrlx1tag > INSIDE && ctrlx2tag > INSIDE) - { - return false; // All points right - } - if (y1tag > INSIDE && y2tag > INSIDE && - ctrly1tag > INSIDE && ctrly2tag > INSIDE) - { - return false; // All points below - } - - // Test for endpoints on the edge where either the segment - // or the curve is headed "inwards" from them - // Note: These tests are a superset of the fast endpoint tests - // above and thus repeat those tests, but take more time - // and cover more cases - if (inwards(x1tag, x2tag, ctrlx1tag) && - inwards(y1tag, y2tag, ctrly1tag)) - { - // First endpoint on border with either edge moving inside - return true; - } - if (inwards(x2tag, x1tag, ctrlx2tag) && - inwards(y2tag, y1tag, ctrly2tag)) - { - // Second endpoint on border with either edge moving inside - return true; - } - - // Trivially accept if endpoints span directly across the rectangle - boolean xoverlap = (x1tag * x2tag <= 0); - boolean yoverlap = (y1tag * y2tag <= 0); - if (x1tag == INSIDE && x2tag == INSIDE && yoverlap) { - return true; - } - if (y1tag == INSIDE && y2tag == INSIDE && xoverlap) { - return true; - } - - // We now know that both endpoints are outside the rectangle - // but the 4 points are not all on one side of the rectangle. - // Therefore the curve cannot be contained inside the rectangle, - // but the rectangle might be contained inside the curve, or - // the curve might intersect the boundary of the rectangle. - - double[] eqn = new double[4]; - double[] res = new double[4]; - if (!yoverlap) { - // Both y coordinates for the closing segment are above or - // below the rectangle which means that we can only intersect - // if the curve crosses the top (or bottom) of the rectangle - // in more than one place and if those crossing locations - // span the horizontal range of the rectangle. - fillEqn(eqn, (y1tag < INSIDE ? y : y+h), y1, ctrly1, ctrly2, y2); - int num = solveCubic(eqn, res); - num = evalCubic(res, num, true, true, null, - x1, ctrlx1, ctrlx2, x2); - // odd counts imply the crossing was out of [0,1] bounds - // otherwise there is no way for that part of the curve to - // "return" to meet its endpoint - return (num == 2 && - getTag(res[0], x, x+w) * getTag(res[1], x, x+w) <= 0); - } - - // Y ranges overlap. Now we examine the X ranges - if (!xoverlap) { - // Both x coordinates for the closing segment are left of - // or right of the rectangle which means that we can only - // intersect if the curve crosses the left (or right) edge - // of the rectangle in more than one place and if those - // crossing locations span the vertical range of the rectangle. - fillEqn(eqn, (x1tag < INSIDE ? x : x+w), x1, ctrlx1, ctrlx2, x2); - int num = solveCubic(eqn, res); - num = evalCubic(res, num, true, true, null, - y1, ctrly1, ctrly2, y2); - // odd counts imply the crossing was out of [0,1] bounds - // otherwise there is no way for that part of the curve to - // "return" to meet its endpoint - return (num == 2 && - getTag(res[0], y, y+h) * getTag(res[1], y, y+h) <= 0); - } - - // The X and Y ranges of the endpoints overlap the X and Y - // ranges of the rectangle, now find out how the endpoint - // line segment intersects the Y range of the rectangle - double dx = x2 - x1; - double dy = y2 - y1; - double k = y2 * x1 - x2 * y1; - int c1tag, c2tag; - if (y1tag == INSIDE) { - c1tag = x1tag; - } else { - c1tag = getTag((k + dx * (y1tag < INSIDE ? y : y+h)) / dy, x, x+w); - } - if (y2tag == INSIDE) { - c2tag = x2tag; - } else { - c2tag = getTag((k + dx * (y2tag < INSIDE ? y : y+h)) / dy, x, x+w); - } - // If the part of the line segment that intersects the Y range - // of the rectangle crosses it horizontally - trivially accept - if (c1tag * c2tag <= 0) { - return true; - } - - // Now we know that both the X and Y ranges intersect and that - // the endpoint line segment does not directly cross the rectangle. - // - // We can almost treat this case like one of the cases above - // where both endpoints are to one side, except that we may - // get one or three intersections of the curve with the vertical - // side of the rectangle. This is because the endpoint segment - // accounts for the other intersection in an even pairing. Thus, - // with the endpoint crossing we end up with 2 or 4 total crossings. - // - // (Remember there is overlap in both the X and Y ranges which - // means that the segment itself must cross at least one vertical - // edge of the rectangle - in particular, the "near vertical side" - // - leaving an odd number of intersections for the curve.) - // - // Now we calculate the y tags of all the intersections on the - // "near vertical side" of the rectangle. We will have one with - // the endpoint segment, and one or three with the curve. If - // any pair of those vertical intersections overlap the Y range - // of the rectangle, we have an intersection. Otherwise, we don't. - - // c1tag = vertical intersection class of the endpoint segment - // - // Choose the y tag of the endpoint that was not on the same - // side of the rectangle as the subsegment calculated above. - // Note that we can "steal" the existing Y tag of that endpoint - // since it will be provably the same as the vertical intersection. - c1tag = ((c1tag * x1tag <= 0) ? y1tag : y2tag); - - // Now we have to calculate an array of solutions of the curve - // with the "near vertical side" of the rectangle. Then we - // need to sort the tags and do a pairwise range test to see - // if either of the pairs of crossings spans the Y range of - // the rectangle. - // - // Note that the c2tag can still tell us which vertical edge - // to test against. - fillEqn(eqn, (c2tag < INSIDE ? x : x+w), x1, ctrlx1, ctrlx2, x2); - int num = solveCubic(eqn, res); - num = evalCubic(res, num, true, true, null, y1, ctrly1, ctrly2, y2); - - // Now put all of the tags into a bucket and sort them. There - // is an intersection iff one of the pairs of tags "spans" the - // Y range of the rectangle. - int tags[] = new int[num+1]; - for (int i = 0; i < num; i++) { - tags[i] = getTag(res[i], y, y+h); - } - tags[num] = c1tag; - Arrays.sort(tags); - return ((num >= 1 && tags[0] * tags[1] <= 0) || - (num >= 3 && tags[2] * tags[3] <= 0)); + int numCrossings = rectCrossings(x, y, w, h); + // the intended return value is + // numCrossings != 0 || numCrossings == Curve.RECT_INTERSECTS + // but if (numCrossings != 0) numCrossings == INTERSECTS won't matter + // and if !(numCrossings != 0) then numCrossings == 0, so + // numCrossings != RECT_INTERSECT + return numCrossings != 0; } /** @@ -1602,20 +1412,32 @@ if (w <= 0 || h <= 0) { return false; } - // Assertion: Cubic curves closed by connecting their - // endpoints form either one or two convex halves with - // the closing line segment as an edge of both sides. - if (!(contains(x, y) && - contains(x + w, y) && - contains(x + w, y + h) && - contains(x, y + h))) { - return false; + + int numCrossings = rectCrossings(x, y, w, h); + return !(numCrossings == 0 || numCrossings == Curve.RECT_INTERSECTS); + } + + private int rectCrossings(double x, double y, double w, double h) { + int crossings = 0; + if (!(getX1() == getX2() && getY1() == getY2())) { + crossings = Curve.rectCrossingsForLine(crossings, + x, y, + x+w, y+h, + getX1(), getY1(), + getX2(), getY2()); + if (crossings == Curve.RECT_INTERSECTS) { + return crossings; + } } - // Either the rectangle is entirely inside one of the convex - // halves or it crosses from one to the other, in which case - // it must intersect the closing line segment. - Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); - return !rect.intersectsLine(getX1(), getY1(), getX2(), getY2()); + // we call this with the curve's direction reversed, because we wanted + // to call rectCrossingsForLine first, because it's cheaper. + return Curve.rectCrossingsForCubic(crossings, + x, y, + x+w, y+h, + getX2(), getY2(), + getCtrlX2(), getCtrlY2(), + getCtrlX1(), getCtrlY1(), + getX1(), getY1(), 0); } /**
--- a/jdk/src/share/classes/java/io/ObjectStreamClass.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/io/ObjectStreamClass.java Wed Feb 09 09:32:04 2011 -0500 @@ -329,7 +329,7 @@ entry = th; } if (future.set(entry)) { - Caches.localDescs.put(key, new SoftReference<>(entry)); + Caches.localDescs.put(key, new SoftReference<Object>(entry)); } else { // nested lookup call already set future entry = future.get(); @@ -2118,7 +2118,7 @@ entry = th; } future.set(entry); - Caches.reflectors.put(key, new SoftReference<>(entry)); + Caches.reflectors.put(key, new SoftReference<Object>(entry)); } if (entry instanceof FieldReflector) {
--- a/jdk/src/share/classes/java/io/PrintStream.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/io/PrintStream.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,9 @@ import java.util.Formatter; import java.util.Locale; - +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; /** * A <code>PrintStream</code> adds functionality to another output stream, @@ -56,7 +58,7 @@ implements Appendable, Closeable { - private boolean autoFlush = false; + private final boolean autoFlush; private boolean trouble = false; private Formatter formatter; @@ -68,6 +70,60 @@ private OutputStreamWriter charOut; /** + * nonNull is explicitly declared here so as not to create an extra + * dependency on java.util.Objects.nonNull. PrintStream is loaded + * early during system initialization. + */ + private static <T> T nonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } + + /** + * Returns a charset object for the given charset name. + * @throws NullPointerException is csn is null + * @throws UnsupportedEncodingException if the charset is not supported + */ + private static Charset toCharset(String csn) + throws UnsupportedEncodingException + { + nonNull(csn, "charsetName"); + try { + return Charset.forName(csn); + } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) { + // UnsupportedEncodingException should be thrown + throw new UnsupportedEncodingException(csn); + } + } + + /* Private constructors */ + private PrintStream(boolean autoFlush, OutputStream out) { + super(out); + this.autoFlush = autoFlush; + this.charOut = new OutputStreamWriter(this); + this.textOut = new BufferedWriter(charOut); + } + + private PrintStream(boolean autoFlush, OutputStream out, Charset charset) { + super(out); + this.autoFlush = autoFlush; + this.charOut = new OutputStreamWriter(this, charset); + this.textOut = new BufferedWriter(charOut); + } + + /* Variant of the private constructor so that the given charset name + * can be verified before evaluating the OutputStream argument. Used + * by constructors creating a FileOutputStream that also take a + * charset name. + */ + private PrintStream(boolean autoFlush, Charset charset, OutputStream out) + throws UnsupportedEncodingException + { + this(autoFlush, out, charset); + } + + /** * Creates a new print stream. This stream will not flush automatically. * * @param out The output stream to which values and objects will be @@ -79,27 +135,6 @@ this(out, false); } - /* Initialization is factored into a private constructor (note the swapped - * parameters so that this one isn't confused with the public one) and a - * separate init method so that the following two public constructors can - * share code. We use a separate init method so that the constructor that - * takes an encoding will throw an NPE for a null stream before it throws - * an UnsupportedEncodingException for an unsupported encoding. - */ - - private PrintStream(boolean autoFlush, OutputStream out) - { - super(out); - if (out == null) - throw new NullPointerException("Null output stream"); - this.autoFlush = autoFlush; - } - - private void init(OutputStreamWriter osw) { - this.charOut = osw; - this.textOut = new BufferedWriter(osw); - } - /** * Creates a new print stream. * @@ -113,8 +148,7 @@ * @see java.io.PrintWriter#PrintWriter(java.io.OutputStream, boolean) */ public PrintStream(OutputStream out, boolean autoFlush) { - this(autoFlush, out); - init(new OutputStreamWriter(this)); + this(autoFlush, nonNull(out, "Null output stream")); } /** @@ -138,8 +172,9 @@ public PrintStream(OutputStream out, boolean autoFlush, String encoding) throws UnsupportedEncodingException { - this(autoFlush, out); - init(new OutputStreamWriter(this, encoding)); + this(autoFlush, + nonNull(out, "Null output stream"), + toCharset(encoding)); } /** @@ -171,7 +206,6 @@ */ public PrintStream(String fileName) throws FileNotFoundException { this(false, new FileOutputStream(fileName)); - init(new OutputStreamWriter(this)); } /** @@ -210,8 +244,8 @@ public PrintStream(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(false, new FileOutputStream(fileName)); - init(new OutputStreamWriter(this, csn)); + // ensure charset is checked before the file is opened + this(false, toCharset(csn), new FileOutputStream(fileName)); } /** @@ -243,7 +277,6 @@ */ public PrintStream(File file) throws FileNotFoundException { this(false, new FileOutputStream(file)); - init(new OutputStreamWriter(this)); } /** @@ -282,8 +315,8 @@ public PrintStream(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(false, new FileOutputStream(file)); - init(new OutputStreamWriter(this, csn)); + // ensure charset is checked before the file is opened + this(false, toCharset(csn), new FileOutputStream(file)); } /** Check to make sure that the stream has not been closed */
--- a/jdk/src/share/classes/java/io/PrintWriter.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/io/PrintWriter.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,12 @@ package java.io; +import java.util.Objects; import java.util.Formatter; import java.util.Locale; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; /** * Prints formatted representations of objects to a text-output stream. This @@ -59,7 +63,7 @@ */ protected Writer out; - private boolean autoFlush = false; + private final boolean autoFlush; private boolean trouble = false; private Formatter formatter; private PrintStream psOut = null; @@ -68,7 +72,24 @@ * Line separator string. This is the value of the line.separator * property at the moment that the stream was created. */ - private String lineSeparator; + private final String lineSeparator; + + /** + * Returns a charset object for the given charset name. + * @throws NullPointerException is csn is null + * @throws UnsupportedEncodingException if the charset is not supported + */ + private static Charset toCharset(String csn) + throws UnsupportedEncodingException + { + Objects.nonNull(csn, "charsetName"); + try { + return Charset.forName(csn); + } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) { + // UnsupportedEncodingException should be thrown + throw new UnsupportedEncodingException(csn); + } + } /** * Creates a new PrintWriter, without automatic line flushing. @@ -164,6 +185,14 @@ false); } + /* Private constructor */ + private PrintWriter(Charset charset, File file) + throws FileNotFoundException + { + this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)), + false); + } + /** * Creates a new PrintWriter, without automatic line flushing, with the * specified file name and charset. This convenience constructor creates @@ -200,8 +229,7 @@ public PrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), csn)), - false); + this(toCharset(csn), new File(fileName)); } /** @@ -272,8 +300,7 @@ public PrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), csn)), - false); + this(toCharset(csn), file); } /** Checks to make sure that the stream has not been closed */
--- a/jdk/src/share/classes/java/lang/AutoCloseable.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/lang/AutoCloseable.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ */ public interface AutoCloseable { /** - * Close this resource, relinquishing any underlying resources. + * Closes this resource, relinquishing any underlying resources. * This method is invoked automatically by the {@code * try}-with-resources statement. * @@ -48,6 +48,10 @@ * visible side effect, unlike {@code Closeable.close} which is * required to have no effect if called more than once. * + * However, while not required to be idempotent, implementers of + * this interface are strongly encouraged to make their {@code + * close} methods idempotent. + * * @throws Exception if this resource cannot be closed */ void close() throws Exception;
--- a/jdk/src/share/classes/java/lang/StringCoding.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/lang/StringCoding.java Wed Feb 09 09:32:04 2011 -0500 @@ -67,7 +67,7 @@ } private static <T> void set(ThreadLocal<SoftReference<T>> tl, T ob) { - tl.set(new SoftReference<>(ob)); + tl.set(new SoftReference<T>(ob)); } // Trim the given byte array to the given length
--- a/jdk/src/share/classes/java/lang/Throwable.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/lang/Throwable.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -809,7 +809,7 @@ native StackTraceElement getStackTraceElement(int index); /** - * Read a {@code Throwable} from a stream, enforcing + * Reads a {@code Throwable} from a stream, enforcing * well-formedness constraints on fields. Null entries and * self-pointers are not allowed in the list of {@code * suppressedExceptions}. Null entries are not allowed for stack @@ -865,9 +865,10 @@ } /** - * Adds the specified exception to the list of exceptions that - * were suppressed, typically by the {@code try}-with-resources - * statement, in order to deliver this exception. + * Appends the specified exception to the exceptions that were + * suppressed in order to deliver this exception. This method is + * typically called (automatically and implicitly) by the {@code + * try}-with-resources statement. * * If the first exception to be suppressed is {@code null}, that * indicates suppressed exception information will <em>not</em> be
--- a/jdk/src/share/classes/java/net/InetAddress.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/net/InetAddress.java Wed Feb 09 09:32:04 2011 -0500 @@ -677,8 +677,7 @@ static InetAddressImpl impl; - private static HashMap<String, InetAddress[]> lookupTable - = new HashMap<String, InetAddress[]>(); + private static final HashMap<String, Void> lookupTable = new HashMap<>(); /** * Represents a cache entry @@ -737,7 +736,7 @@ // As we iterate in insertion order we can // terminate when a non-expired entry is found. - LinkedList<String> expired = new LinkedList<String>(); + LinkedList<String> expired = new LinkedList<>(); long now = System.currentTimeMillis(); for (String key : cache.keySet()) { CacheEntry entry = cache.get(key); @@ -1227,43 +1226,45 @@ // lookupTable and return null so the // following code would do a lookup itself. if ((addresses = checkLookupTable(host)) == null) { - // This is the first thread which looks up the addresses - // this host or the cache entry for this host has been - // expired so this thread should do the lookup. - for (NameService nameService : nameServices) { - try { - /* - * Do not put the call to lookup() inside the - * constructor. if you do you will still be - * allocating space when the lookup fails. - */ + try { + // This is the first thread which looks up the addresses + // this host or the cache entry for this host has been + // expired so this thread should do the lookup. + for (NameService nameService : nameServices) { + try { + /* + * Do not put the call to lookup() inside the + * constructor. if you do you will still be + * allocating space when the lookup fails. + */ - addresses = nameService.lookupAllHostAddr(host); - success = true; - break; - } catch (UnknownHostException uhe) { - if (host.equalsIgnoreCase("localhost")) { - InetAddress[] local = new InetAddress[] { impl.loopbackAddress() }; - addresses = local; + addresses = nameService.lookupAllHostAddr(host); success = true; break; - } - else { - addresses = unknown_array; - success = false; - ex = uhe; + } catch (UnknownHostException uhe) { + if (host.equalsIgnoreCase("localhost")) { + InetAddress[] local = new InetAddress[] { impl.loopbackAddress() }; + addresses = local; + success = true; + break; + } + else { + addresses = unknown_array; + success = false; + ex = uhe; + } } } + + // Cache the addresses. + cacheAddresses(host, addresses, success); + if (!success && ex != null) + throw ex; + } finally { + // Delete host from the lookupTable and notify + // all threads waiting on the lookupTable monitor. + updateLookupTable(host); } - - // Cache the addresses. - cacheAddresses(host, addresses, success); - // Delete the host from the lookupTable, and - // notify all threads waiting for the monitor - // for lookupTable. - updateLookupTable(host); - if (!success && ex != null) - throw ex; } return addresses; @@ -1271,16 +1272,13 @@ private static InetAddress[] checkLookupTable(String host) { - // make sure addresses is null. - InetAddress[] addresses = null; - synchronized (lookupTable) { // If the host isn't in the lookupTable, add it in the // lookuptable and return null. The caller should do // the lookup. if (lookupTable.containsKey(host) == false) { lookupTable.put(host, null); - return addresses; + return null; } // If the host is in the lookupTable, it means that another @@ -1298,10 +1296,11 @@ // the host. This thread should retry to get the addresses // from the addressCache. If it doesn't get the addresses from // the cache, it will try to look up the addresses itself. - addresses = getCachedAddresses(host); + InetAddress[] addresses = getCachedAddresses(host); if (addresses == null) { synchronized (lookupTable) { lookupTable.put(host, null); + return null; } }
--- a/jdk/src/share/classes/java/net/NetworkInterface.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/net/NetworkInterface.java Wed Feb 09 09:32:04 2011 -0500 @@ -493,55 +493,44 @@ * @see java.net.InetAddress#getAddress() */ public boolean equals(Object obj) { - if ((obj == null) || !(obj instanceof NetworkInterface)) { + if (!(obj instanceof NetworkInterface)) { return false; } - NetworkInterface netIF = (NetworkInterface)obj; - if (name != null ) { - if (netIF.getName() != null) { - if (!name.equals(netIF.getName())) { - return false; - } - } else { + NetworkInterface that = (NetworkInterface)obj; + if (this.name != null ) { + if (!this.name.equals(that.name)) { return false; } } else { - if (netIF.getName() != null) { + if (that.name != null) { return false; } } - Enumeration newAddrs = netIF.getInetAddresses(); - int i = 0; - for (i = 0; newAddrs.hasMoreElements();newAddrs.nextElement(), i++); - if (addrs == null) { - if (i != 0) { - return false; - } - } else { - /* - * Compare number of addresses (in the checked subset) - */ - int count = 0; - Enumeration e = getInetAddresses(); - for (; e.hasMoreElements(); count++) { - e.nextElement(); - } - if (i != count) { - return false; - } + + if (this.addrs == null) { + return that.addrs == null; + } else if (that.addrs == null) { + return false; } - newAddrs = netIF.getInetAddresses(); - for (; newAddrs.hasMoreElements();) { - boolean equal = false; - Enumeration thisAddrs = getInetAddresses(); - InetAddress newAddr = (InetAddress)newAddrs.nextElement(); - for (; thisAddrs.hasMoreElements();) { - InetAddress thisAddr = (InetAddress)thisAddrs.nextElement(); - if (thisAddr.equals(newAddr)) { - equal = true; + + /* Both addrs not null. Compare number of addresses */ + + if (this.addrs.length != that.addrs.length) { + return false; + } + + InetAddress[] thatAddrs = that.addrs; + int count = thatAddrs.length; + + for (int i=0; i<count; i++) { + boolean found = false; + for (int j=0; j<count; j++) { + if (addrs[i].equals(thatAddrs[j])) { + found = true; + break; } } - if (!equal) { + if (!found) { return false; } } @@ -549,12 +538,7 @@ } public int hashCode() { - int count = name == null? 0: name.hashCode(); - Enumeration<InetAddress> addrs = getInetAddresses(); - while (addrs.hasMoreElements()) { - count += addrs.nextElement().hashCode(); - } - return count; + return name == null? 0: name.hashCode(); } public String toString() {
--- a/jdk/src/share/classes/java/net/SocksSocketImpl.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/net/SocksSocketImpl.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.BufferedOutputStream; import java.security.AccessController; import java.security.PrivilegedExceptionAction; +import sun.net.SocksProxy; import sun.net.www.ParseUtil; /* import org.ietf.jgss.*; */ @@ -397,6 +398,11 @@ // Use getHostString() to avoid reverse lookups server = ((InetSocketAddress) p.address()).getHostString(); serverPort = ((InetSocketAddress) p.address()).getPort(); + if (p instanceof SocksProxy) { + if (((SocksProxy)p).protocolVersion() == 4) { + useV4 = true; + } + } // Connects to the SOCKS server try { @@ -700,6 +706,11 @@ // Use getHostString() to avoid reverse lookups server = ((InetSocketAddress) p.address()).getHostString(); serverPort = ((InetSocketAddress) p.address()).getPort(); + if (p instanceof SocksProxy) { + if (((SocksProxy)p).protocolVersion() == 4) { + useV4 = true; + } + } // Connects to the SOCKS server try {
--- a/jdk/src/share/classes/java/net/URLClassLoader.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/net/URLClassLoader.java Wed Feb 09 09:32:04 2011 -0500 @@ -27,19 +27,15 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.io.File; -import java.io.FilePermission; -import java.io.InputStream; -import java.io.IOException; -import java.io.Closeable; +import java.lang.ref.*; +import java.io.*; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandlerFactory; import java.util.Enumeration; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; +import java.util.*; import java.util.jar.Manifest; +import java.util.jar.JarFile; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.security.CodeSigner; @@ -194,6 +190,65 @@ acc = AccessController.getContext(); } + /* A map (used as a set) to keep track of closeable local resources + * (either JarFiles or FileInputStreams). We don't care about + * Http resources since they don't need to be closed. + * + * If the resource is coming from a jar file + * we keep a (weak) reference to the JarFile object which can + * be closed if URLClassLoader.close() called. Due to jar file + * caching there will typically be only one JarFile object + * per underlying jar file. + * + * For file resources, which is probably a less common situation + * we have to keep a weak reference to each stream. + */ + + private WeakHashMap<Closeable,Void> + closeables = new WeakHashMap<>(); + + /** + * Returns an input stream for reading the specified resource. + * If this loader is closed, then any resources opened by this method + * will be closed. + * + * <p> The search order is described in the documentation for {@link + * #getResource(String)}. </p> + * + * @param name + * The resource name + * + * @return An input stream for reading the resource, or <tt>null</tt> + * if the resource could not be found + * + * @since 1.7 + */ + public InputStream getResourceAsStream(String name) { + URL url = getResource(name); + try { + if (url == null) { + return null; + } + URLConnection urlc = url.openConnection(); + InputStream is = urlc.getInputStream(); + if (urlc instanceof JarURLConnection) { + JarURLConnection juc = (JarURLConnection)urlc; + JarFile jar = juc.getJarFile(); + synchronized (closeables) { + if (!closeables.containsKey(jar)) { + closeables.put(jar, null); + } + } + } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) { + synchronized (closeables) { + closeables.put(is, null); + } + } + return is; + } catch (IOException e) { + return null; + } + } /** * Closes this URLClassLoader, so that it can no longer be used to load @@ -202,8 +257,8 @@ * delegation hierarchy are still accessible. Also, any classes or resources * that are already loaded, are still accessible. * <p> - * In the case of jar: and file: URLs, it also closes any class files, - * or JAR files that were opened by it. If another thread is loading a + * In the case of jar: and file: URLs, it also closes any files + * that were opened by it. If another thread is loading a * class when the {@code close} method is invoked, then the result of * that load is undefined. * <p> @@ -213,10 +268,10 @@ * loader has no effect. * <p> * @throws IOException if closing any file opened by this class loader - * resulted in an IOException. Any such exceptions are caught, and a - * single IOException is thrown after the last file has been closed. - * If only one exception was thrown, it will be set as the <i>cause</i> - * of this IOException. + * resulted in an IOException. Any such exceptions are caught internally. + * If only one is caught, then it is re-thrown. If more than one exception + * is caught, then the second and following exceptions are added + * as suppressed exceptions of the first one caught, which is then re-thrown. * * @throws SecurityException if a security manager is set, and it denies * {@link RuntimePermission}<tt>("closeClassLoader")</tt> @@ -229,21 +284,33 @@ security.checkPermission(new RuntimePermission("closeClassLoader")); } List<IOException> errors = ucp.closeLoaders(); + + // now close any remaining streams. + + synchronized (closeables) { + Set<Closeable> keys = closeables.keySet(); + for (Closeable c : keys) { + try { + c.close(); + } catch (IOException ioex) { + errors.add(ioex); + } + } + closeables.clear(); + } + if (errors.isEmpty()) { return; } - if (errors.size() == 1) { - throw new IOException ( - "Error closing URLClassLoader resource", - errors.get(0) - ); + + IOException firstex = errors.remove(0); + + // Suppress any remaining exceptions + + for (IOException error: errors) { + firstex.addSuppressed(error); } - // Several exceptions. So, just combine the error messages - String errormsg = "Error closing resources: "; - for (IOException error: errors) { - errormsg = errormsg + "[" + error.toString() + "] "; - } - throw new IOException (errormsg); + throw firstex; } /**
--- a/jdk/src/share/classes/java/net/doc-files/net-properties.html Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/net/doc-files/net-properties.html Wed Feb 09 09:32:04 2011 -0500 @@ -127,10 +127,15 @@ are specified. If SOCKS is supported by a Java SE implementation, the following properties will be used:</P> <UL> - <LI><P><B>socksProxyHost</B> (default: <non>)<BR> + <LI><P><B>socksProxyHost</B> (default: <none>)<BR> The hostname, or address, of the proxy server.</P> <LI><P><B>socksProxyPort</B> (default: 1080)<BR> The port number of the proxy server.</P> + <LI><P><B>socksProxyVersion</B> (default: 5)<BR> + The version of the SOCKS protocol supported by the server. The + default is <code>5</code> indicating SOCKS V5, alternatively + <code>4</code> can be specified for SOCKS V4. Setting the property + to values other than these leads to unspecified behavior.</P> <LI><P><B>java.net.socks.username</B> (default: <none>)<BR> Username to use if the SOCKSv5 server asks for authentication and no java.net.Authenticator instance was found.</P>
--- a/jdk/src/share/classes/java/security/AccessControlContext.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/AccessControlContext.java Wed Feb 09 09:32:04 2011 -0500 @@ -121,7 +121,7 @@ this.context = null; } } else { - List<ProtectionDomain> v = new ArrayList<ProtectionDomain>(context.length); + List<ProtectionDomain> v = new ArrayList<>(context.length); for (int i =0; i< context.length; i++) { if ((context[i] != null) && (!v.contains(context[i]))) v.add(context[i]);
--- a/jdk/src/share/classes/java/security/BasicPermission.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/BasicPermission.java Wed Feb 09 09:32:04 2011 -0500 @@ -515,7 +515,7 @@ // Copy perms into a Hashtable Hashtable<String, Permission> permissions = - new Hashtable<String, Permission>(perms.size()*2); + new Hashtable<>(perms.size()*2); synchronized (this) { permissions.putAll(perms);
--- a/jdk/src/share/classes/java/security/CodeSource.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/CodeSource.java Wed Feb 09 09:32:04 2011 -0500 @@ -188,7 +188,7 @@ } else if (signers != null) { // Convert the code signers to certs ArrayList<java.security.cert.Certificate> certChains = - new ArrayList<java.security.cert.Certificate>(); + new ArrayList<>(); for (int i = 0; i < signers.length; i++) { certChains.addAll( signers[i].getSignerCertPath().getCertificates()); @@ -606,10 +606,10 @@ // Iterate through all the certificates int i = 0; - List<CodeSigner> signers = new ArrayList<CodeSigner>(); + List<CodeSigner> signers = new ArrayList<>(); while (i < certs.length) { List<java.security.cert.Certificate> certChain = - new ArrayList<java.security.cert.Certificate>(); + new ArrayList<>(); certChain.add(certs[i++]); // first cert is an end-entity cert int j = i;
--- a/jdk/src/share/classes/java/security/Permissions.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/Permissions.java Wed Feb 09 09:32:04 2011 -0500 @@ -362,7 +362,7 @@ // Copy perms into a Hashtable Hashtable<Class<?>, PermissionCollection> perms = - new Hashtable<Class<?>, PermissionCollection>(permsMap.size()*2); // no sync; estimate + new Hashtable<>(permsMap.size()*2); // no sync; estimate synchronized (this) { perms.putAll(permsMap); } @@ -567,7 +567,7 @@ // Copy perms into a Hashtable Hashtable<Permission, Permission> perms = - new Hashtable<Permission, Permission>(permsMap.size()*2); + new Hashtable<>(permsMap.size()*2); synchronized (this) { perms.putAll(permsMap); }
--- a/jdk/src/share/classes/java/security/ProtectionDomain.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/ProtectionDomain.java Wed Feb 09 09:32:04 2011 -0500 @@ -336,8 +336,8 @@ int swag = 32; int vcap = 8; Enumeration<Permission> e; - List<Permission> pdVector = new ArrayList<Permission>(vcap); - List<Permission> plVector = new ArrayList<Permission>(swag); + List<Permission> pdVector = new ArrayList<>(vcap); + List<Permission> plVector = new ArrayList<>(swag); // // Build a vector of domain permissions for subsequent merge
--- a/jdk/src/share/classes/java/security/Provider.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/Provider.java Wed Feb 09 09:32:04 2011 -0500 @@ -437,7 +437,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - Map<Object,Object> copy = new HashMap<Object,Object>(); + Map<Object,Object> copy = new HashMap<>(); for (Map.Entry<Object,Object> entry : super.entrySet()) { copy.put(entry.getKey(), entry.getValue()); } @@ -719,7 +719,7 @@ } if (serviceSet == null) { ensureLegacyParsed(); - Set<Service> set = new LinkedHashSet<Service>(); + Set<Service> set = new LinkedHashSet<>(); if (serviceMap != null) { set.addAll(serviceMap.values()); } @@ -1395,7 +1395,7 @@ if (s != null) { String[] classNames = s.split("\\|"); List<Class> classList = - new ArrayList<Class>(classNames.length); + new ArrayList<>(classNames.length); for (String className : classNames) { Class clazz = getKeyClass(className); if (clazz != null) {
--- a/jdk/src/share/classes/java/security/SecureClassLoader.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/SecureClassLoader.java Wed Feb 09 09:32:04 2011 -0500 @@ -50,7 +50,7 @@ // HashMap that maps CodeSource to ProtectionDomain // @GuardedBy("pdcache") private final HashMap<CodeSource, ProtectionDomain> pdcache = - new HashMap<CodeSource, ProtectionDomain>(11); + new HashMap<>(11); private static final Debug debug = Debug.getInstance("scl");
--- a/jdk/src/share/classes/java/security/Security.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/Security.java Wed Feb 09 09:32:04 2011 -0500 @@ -545,8 +545,7 @@ value = filter.substring(index + 1); } - Hashtable<String, String> hashtableFilter = - new Hashtable<String, String>(1); + Hashtable<String, String> hashtableFilter = new Hashtable<>(1); hashtableFilter.put(key, value); return (getProviders(hashtableFilter)); @@ -606,7 +605,7 @@ // Then only return those providers who satisfy the selection criteria. Provider[] allProviders = Security.getProviders(); Set<String> keySet = filter.keySet(); - LinkedHashSet<Provider> candidates = new LinkedHashSet<Provider>(5); + LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5); // Returns all installed providers // if the selection criteria is null. @@ -660,8 +659,7 @@ } // Map containing cached Spi Class objects of the specified type - private static final Map<String,Class> spiMap = - new ConcurrentHashMap<String,Class>(); + private static final Map<String, Class> spiMap = new ConcurrentHashMap<>(); /** * Return the Class object for the given engine type @@ -885,7 +883,7 @@ String attrName, String filterValue, Provider[] allProviders) { - LinkedHashSet<Provider> candidates = new LinkedHashSet<Provider>(5); + LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5); for (int i = 0; i < allProviders.length; i++) { if (isCriterionSatisfied(allProviders[i], serviceName, algName, @@ -1082,7 +1080,7 @@ return Collections.EMPTY_SET; } - HashSet<String> result = new HashSet<String>(); + HashSet<String> result = new HashSet<>(); Provider[] providers = Security.getProviders(); for (int i = 0; i < providers.length; i++) {
--- a/jdk/src/share/classes/java/security/UnresolvedPermission.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/UnresolvedPermission.java Wed Feb 09 09:32:04 2011 -0500 @@ -198,7 +198,7 @@ if (this.certs == null) { // extract the signer certs ArrayList<java.security.cert.Certificate> signerCerts = - new ArrayList<java.security.cert.Certificate>(); + new ArrayList<>(); i = 0; while (i < certs.length) { signerCerts.add(certs[i]);
--- a/jdk/src/share/classes/java/security/UnresolvedPermissionCollection.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/security/UnresolvedPermissionCollection.java Wed Feb 09 09:32:04 2011 -0500 @@ -119,7 +119,7 @@ public Enumeration<Permission> elements() { List<Permission> results = - new ArrayList<Permission>(); // where results are stored + new ArrayList<>(); // where results are stored // Get iterator of Map values (which are lists of permissions) synchronized (this) { @@ -161,7 +161,7 @@ // Copy perms into a Hashtable Hashtable<String, Vector<UnresolvedPermission>> permissions = - new Hashtable<String, Vector<UnresolvedPermission>>(perms.size()*2); + new Hashtable<>(perms.size()*2); // Convert each entry (List) into a Vector synchronized (this) { @@ -169,8 +169,7 @@ for (Map.Entry<String, List<UnresolvedPermission>> e : set) { // Convert list into Vector List<UnresolvedPermission> list = e.getValue(); - Vector<UnresolvedPermission> vec = - new Vector<UnresolvedPermission>(list.size()); + Vector<UnresolvedPermission> vec = new Vector<>(list.size()); synchronized (list) { vec.addAll(list); } @@ -207,8 +206,7 @@ for (Map.Entry<String, Vector<UnresolvedPermission>> e : set) { // Convert Vector into ArrayList Vector<UnresolvedPermission> vec = e.getValue(); - List<UnresolvedPermission> list = - new ArrayList<UnresolvedPermission>(vec.size()); + List<UnresolvedPermission> list = new ArrayList<>(vec.size()); list.addAll(vec); // Add to Hashtable being serialized
--- a/jdk/src/share/classes/java/sql/Timestamp.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/sql/Timestamp.java Wed Feb 09 09:32:04 2011 -0500 @@ -473,7 +473,9 @@ * @since 1.4 */ public int compareTo(Timestamp ts) { - int i = super.compareTo(ts); + long thisTime = this.getTime(); + long anotherTime = ts.getTime(); + int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1)); if (i == 0) { if (nanos > ts.nanos) { return 1;
--- a/jdk/src/share/classes/java/util/Collections.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/Collections.java Wed Feb 09 09:32:04 2011 -0500 @@ -1452,10 +1452,10 @@ * when o is a Map.Entry, and calls o.setValue. */ public boolean containsAll(Collection<?> coll) { - Iterator<?> it = coll.iterator(); - while (it.hasNext()) - if (!contains(it.next())) // Invokes safe contains() above + for (Object e : coll) { + if (!contains(e)) // Invokes safe contains() above return false; + } return true; } public boolean equals(Object o) { @@ -3713,45 +3713,91 @@ } /** - * Returns <tt>true</tt> if the two specified collections have no + * Returns {@code true} if the two specified collections have no * elements in common. * * <p>Care must be exercised if this method is used on collections that - * do not comply with the general contract for <tt>Collection</tt>. + * do not comply with the general contract for {@code Collection}. * Implementations may elect to iterate over either collection and test * for containment in the other collection (or to perform any equivalent * computation). If either collection uses a nonstandard equality test - * (as does a {@link SortedSet} whose ordering is not <i>compatible with - * equals</i>, or the key set of an {@link IdentityHashMap}), both + * (as does a {@link SortedSet} whose ordering is not <em>compatible with + * equals</em>, or the key set of an {@link IdentityHashMap}), both * collections must use the same nonstandard equality test, or the * result of this method is undefined. * + * <p>Care must also be exercised when using collections that have + * restrictions on the elements that they may contain. Collection + * implementations are allowed to throw exceptions for any operation + * involving elements they deem ineligible. For absolute safety the + * specified collections should contain only elements which are + * eligible elements for both collections. + * * <p>Note that it is permissible to pass the same collection in both - * parameters, in which case the method will return true if and only if - * the collection is empty. + * parameters, in which case the method will return {@code true} if and + * only if the collection is empty. * * @param c1 a collection * @param c2 a collection - * @throws NullPointerException if either collection is null + * @return {@code true} if the two specified collections have no + * elements in common. + * @throws NullPointerException if either collection is {@code null}. + * @throws NullPointerException if one collection contains a {@code null} + * element and {@code null} is not an eligible element for the other collection. + * (optional) + * @throws ClassCastException if one collection contains an element that is + * of a type which is ineligible for the other collection. (optional) * @since 1.5 */ public static boolean disjoint(Collection<?> c1, Collection<?> c2) { - /* - * We're going to iterate through c1 and test for inclusion in c2. - * If c1 is a Set and c2 isn't, swap the collections. Otherwise, - * place the shorter collection in c1. Hopefully this heuristic - * will minimize the cost of the operation. - */ - if ((c1 instanceof Set) && !(c2 instanceof Set) || - (c1.size() > c2.size())) { - Collection<?> tmp = c1; - c1 = c2; - c2 = tmp; + // The collection to be used for contains(). Preference is given to + // the collection who's contains() has lower O() complexity. + Collection<?> contains = c2; + // The collection to be iterated. If the collections' contains() impl + // are of different O() complexity, the collection with slower + // contains() will be used for iteration. For collections who's + // contains() are of the same complexity then best performance is + // achieved by iterating the smaller collection. + Collection<?> iterate = c1; + + // Performance optimization cases. The heuristics: + // 1. Generally iterate over c1. + // 2. If c1 is a Set then iterate over c2. + // 3. If either collection is empty then result is always true. + // 4. Iterate over the smaller Collection. + if (c1 instanceof Set) { + // Use c1 for contains as a Set's contains() is expected to perform + // better than O(N/2) + iterate = c2; + contains = c1; + } else if (!(c2 instanceof Set)) { + // Both are mere Collections. Iterate over smaller collection. + // Example: If c1 contains 3 elements and c2 contains 50 elements and + // assuming contains() requires ceiling(N/2) comparisons then + // checking for all c1 elements in c2 would require 75 comparisons + // (3 * ceiling(50/2)) vs. checking all c2 elements in c1 requiring + // 100 comparisons (50 * ceiling(3/2)). + int c1size = c1.size(); + int c2size = c2.size(); + if (c1size == 0 || c2size == 0) { + // At least one collection is empty. Nothing will match. + return true; + } + + if (c1size > c2size) { + iterate = c2; + contains = c1; + } } - for (Object e : c1) - if (c2.contains(e)) + for (Object e : iterate) { + if (contains.contains(e)) { + // Found a common element. Collections are not disjoint. return false; + } + } + + // No common elements were found. return true; }
--- a/jdk/src/share/classes/java/util/Formatter.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/Formatter.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,8 @@ import java.math.MathContext; import java.math.RoundingMode; import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; import java.text.DateFormatSymbols; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -1838,22 +1840,53 @@ */ public final class Formatter implements Closeable, Flushable { private Appendable a; - private Locale l; + private final Locale l; private IOException lastException; - private char zero = '0'; + private final char zero; private static double scaleUp; // 1 (sign) + 19 (max # sig digits) + 1 ('.') + 1 ('e') + 1 (sign) // + 3 (max # exp digits) + 4 (error) = 30 private static final int MAX_FD_CHARS = 30; - // Initialize internal data. - private void init(Appendable a, Locale l) { + /** + * Returns a charset object for the given charset name. + * @throws NullPointerException is csn is null + * @throws UnsupportedEncodingException if the charset is not supported + */ + private static Charset toCharset(String csn) + throws UnsupportedEncodingException + { + Objects.nonNull(csn, "charsetName"); + try { + return Charset.forName(csn); + } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) { + // UnsupportedEncodingException should be thrown + throw new UnsupportedEncodingException(csn); + } + } + + private static final Appendable nonNullAppendable(Appendable a) { + if (a == null) + return new StringBuilder(); + + return a; + } + + /* Private constructors */ + private Formatter(Locale l, Appendable a) { this.a = a; this.l = l; - setZero(); + this.zero = getZero(l); + } + + private Formatter(Charset charset, Locale l, File file) + throws FileNotFoundException + { + this(l, + new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset))); } /** @@ -1867,7 +1900,7 @@ * virtual machine. */ public Formatter() { - init(new StringBuilder(), Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), new StringBuilder()); } /** @@ -1881,9 +1914,7 @@ * {@code null} then a {@link StringBuilder} will be created. */ public Formatter(Appendable a) { - if (a == null) - a = new StringBuilder(); - init(a, Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), nonNullAppendable(a)); } /** @@ -1900,7 +1931,7 @@ * is applied. */ public Formatter(Locale l) { - init(new StringBuilder(), l); + this(l, new StringBuilder()); } /** @@ -1916,9 +1947,7 @@ * is applied. */ public Formatter(Appendable a, Locale l) { - if (a == null) - a = new StringBuilder(); - init(a, l); + this(l, nonNullAppendable(a)); } /** @@ -1949,8 +1978,8 @@ * creating the file */ public Formatter(String fileName) throws FileNotFoundException { - init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))), - Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), + new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)))); } /** @@ -2025,8 +2054,7 @@ public Formatter(String fileName, String csn, Locale l) throws FileNotFoundException, UnsupportedEncodingException { - init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), csn)), - l); + this(toCharset(csn), l, new File(fileName)); } /** @@ -2057,8 +2085,8 @@ * creating the file */ public Formatter(File file) throws FileNotFoundException { - init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))), - Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), + new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)))); } /** @@ -2133,8 +2161,7 @@ public Formatter(File file, String csn, Locale l) throws FileNotFoundException, UnsupportedEncodingException { - init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), csn)), - l); + this(toCharset(csn), l, file); } /** @@ -2151,9 +2178,8 @@ * The stream to use as the destination of this formatter. */ public Formatter(PrintStream ps) { - if (ps == null) - throw new NullPointerException(); - init((Appendable)ps, Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), + (Appendable)Objects.nonNull(ps)); } /** @@ -2171,8 +2197,8 @@ * The output will be buffered. */ public Formatter(OutputStream os) { - init(new BufferedWriter(new OutputStreamWriter(os)), - Locale.getDefault(Locale.Category.FORMAT)); + this(Locale.getDefault(Locale.Category.FORMAT), + new BufferedWriter(new OutputStreamWriter(os))); } /** @@ -2222,13 +2248,15 @@ public Formatter(OutputStream os, String csn, Locale l) throws UnsupportedEncodingException { - init(new BufferedWriter(new OutputStreamWriter(os, csn)), l); + this(l, new BufferedWriter(new OutputStreamWriter(os, csn))); } - private void setZero() { + private static char getZero(Locale l) { if ((l != null) && !l.equals(Locale.US)) { DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l); - zero = dfs.getZeroDigit(); + return dfs.getZeroDigit(); + } else { + return '0'; } }
--- a/jdk/src/share/classes/java/util/LinkedList.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/LinkedList.java Wed Feb 09 09:32:04 2011 -0500 @@ -26,9 +26,9 @@ package java.util; /** - * Linked list implementation of the {@link List} and {@link Deque} interfaces. - * Implements all optional operations, and permits all elements (including - * {@code null}). + * Doubly-linked list implementation of the {@code List} and {@code Deque} + * interfaces. Implements all optional list operations, and permits all + * elements (including {@code null}). * * <p>All of the operations perform as could be expected for a doubly-linked * list. Operations that index into the list will traverse the list from @@ -249,7 +249,7 @@ * @return the last element in this list * @throws NoSuchElementException if this list is empty */ - public E getLast() { + public E getLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException();
--- a/jdk/src/share/classes/java/util/Scanner.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/Scanner.java Wed Feb 09 09:32:04 2011 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -571,10 +571,8 @@ * @return A scanner with the specified source and pattern */ private Scanner(Readable source, Pattern pattern) { - if (source == null) - throw new NullPointerException("source"); - if (pattern == null) - throw new NullPointerException("pattern"); + assert source != null : "source should not be null"; + assert pattern != null : "pattern should not be null"; this.source = source; delimPattern = pattern; buf = CharBuffer.allocate(BUFFER_SIZE); @@ -593,7 +591,7 @@ * interface */ public Scanner(Readable source) { - this(source, WHITESPACE_PATTERN); + this(Objects.nonNull(source, "source"), WHITESPACE_PATTERN); } /** @@ -620,23 +618,27 @@ * does not exist */ public Scanner(InputStream source, String charsetName) { - this(makeReadable(source, charsetName), WHITESPACE_PATTERN); + this(makeReadable(Objects.nonNull(source, "source"), toCharset(charsetName)), + WHITESPACE_PATTERN); } - private static Readable makeReadable(InputStream source, - String charsetName) - { - if (source == null) - throw new NullPointerException("source"); - InputStreamReader isr = null; + /** + * Returns a charset object for the given charset name. + * @throws NullPointerException is csn is null + * @throws IllegalArgumentException if the charset is not supported + */ + private static Charset toCharset(String csn) { + Objects.nonNull(csn, "charsetName"); try { - isr = new InputStreamReader(source, charsetName); - } catch (UnsupportedEncodingException uee) { - IllegalArgumentException iae = new IllegalArgumentException(); - iae.initCause(uee); - throw iae; + return Charset.forName(csn); + } catch (IllegalCharsetNameException|UnsupportedCharsetException e) { + // IllegalArgumentException should be thrown + throw new IllegalArgumentException(e); } - return isr; + } + + private static Readable makeReadable(InputStream source, Charset charset) { + return new InputStreamReader(source, charset); } /** @@ -648,9 +650,7 @@ * @param source A file to be scanned * @throws FileNotFoundException if source is not found */ - public Scanner(File source) - throws FileNotFoundException - { + public Scanner(File source) throws FileNotFoundException { this((ReadableByteChannel)(new FileInputStream(source).getChannel())); } @@ -669,8 +669,27 @@ public Scanner(File source, String charsetName) throws FileNotFoundException { - this((ReadableByteChannel)(new FileInputStream(source).getChannel()), - charsetName); + this(Objects.nonNull(source), toDecoder(charsetName)); + } + + private Scanner(File source, CharsetDecoder dec) + throws FileNotFoundException + { + this(makeReadable((ReadableByteChannel)(new FileInputStream(source).getChannel()), dec)); + } + + private static CharsetDecoder toDecoder(String charsetName) { + Objects.nonNull(charsetName, "charsetName"); + try { + return Charset.forName(charsetName).newDecoder(); + } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) { + throw new IllegalArgumentException(charsetName); + } + } + + private static Readable makeReadable(ReadableByteChannel source, + CharsetDecoder dec) { + return Channels.newReader(source, dec, -1); } /** @@ -708,10 +727,12 @@ * if the specified encoding is not found * @since 1.7 */ - public Scanner(FileRef source, String charsetName) - throws IOException - { - this(source.newInputStream(), charsetName); + public Scanner(FileRef source, String charsetName) throws IOException { + this(Objects.nonNull(source), toCharset(charsetName)); + } + + private Scanner(FileRef source, Charset charset) throws IOException { + this(makeReadable(source.newInputStream(), charset)); } /** @@ -733,16 +754,12 @@ * @param source A channel to scan */ public Scanner(ReadableByteChannel source) { - this(makeReadable(source), WHITESPACE_PATTERN); + this(makeReadable(Objects.nonNull(source, "source")), + WHITESPACE_PATTERN); } private static Readable makeReadable(ReadableByteChannel source) { - if (source == null) - throw new NullPointerException("source"); - String defaultCharsetName = - java.nio.charset.Charset.defaultCharset().name(); - return Channels.newReader(source, - java.nio.charset.Charset.defaultCharset().name()); + return makeReadable(source, Charset.defaultCharset().newDecoder()); } /** @@ -757,17 +774,8 @@ * does not exist */ public Scanner(ReadableByteChannel source, String charsetName) { - this(makeReadable(source, charsetName), WHITESPACE_PATTERN); - } - - private static Readable makeReadable(ReadableByteChannel source, - String charsetName) - { - if (source == null) - throw new NullPointerException("source"); - if (!Charset.isSupported(charsetName)) - throw new IllegalArgumentException(charsetName); - return Channels.newReader(source, charsetName); + this(makeReadable(Objects.nonNull(source, "source"), toDecoder(charsetName)), + WHITESPACE_PATTERN); } // Private primitives used to support scanning
--- a/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Wed Feb 09 09:32:04 2011 -0500 @@ -49,14 +49,14 @@ * <p>This is a classic "bounded buffer", in which a * fixed-sized array holds elements inserted by producers and * extracted by consumers. Once created, the capacity cannot be - * increased. Attempts to <tt>put</tt> an element into a full queue - * will result in the operation blocking; attempts to <tt>take</tt> an + * changed. Attempts to {@code put} an element into a full queue + * will result in the operation blocking; attempts to {@code take} an * element from an empty queue will similarly block. * - * <p> This class supports an optional fairness policy for ordering + * <p>This class supports an optional fairness policy for ordering * waiting producer and consumer threads. By default, this ordering * is not guaranteed. However, a queue constructed with fairness set - * to <tt>true</tt> grants threads access in FIFO order. Fairness + * to {@code true} grants threads access in FIFO order. Fairness * generally decreases throughput but reduces variability and avoids * starvation. * @@ -83,14 +83,17 @@ */ private static final long serialVersionUID = -817911632652898426L; - /** The queued items */ - private final E[] items; - /** items index for next take, poll or remove */ - private int takeIndex; - /** items index for next put, offer, or add. */ - private int putIndex; - /** Number of items in the queue */ - private int count; + /** The queued items */ + final Object[] items; + + /** items index for next take, poll, peek or remove */ + int takeIndex; + + /** items index for next put, offer, or add */ + int putIndex; + + /** Number of elements in the queue */ + int count; /* * Concurrency control uses the classic two-condition algorithm @@ -98,7 +101,7 @@ */ /** Main lock guarding all access */ - private final ReentrantLock lock; + final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ @@ -110,7 +113,36 @@ * Circularly increment i. */ final int inc(int i) { - return (++i == items.length)? 0 : i; + return (++i == items.length) ? 0 : i; + } + + /** + * Circularly decrement i. + */ + final int dec(int i) { + 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]); + } + + /** + * Throws NullPointerException if argument is null. + * + * @param v the element + */ + private static void checkNotNull(Object v) { + if (v == null) + throw new NullPointerException(); } /** @@ -129,8 +161,8 @@ * Call only when holding lock. */ private E extract() { - final E[] items = this.items; - E x = items[takeIndex]; + final Object[] items = this.items; + E x = this.<E>cast(items[takeIndex]); items[takeIndex] = null; takeIndex = inc(takeIndex); --count; @@ -139,11 +171,12 @@ } /** - * Utility for remove and iterator.remove: Delete item at position i. + * Deletes item at position i. + * Utility for remove and iterator.remove. * Call only when holding lock. */ void removeAt(int i) { - final E[] items = this.items; + final Object[] items = this.items; // if removing front item, just advance if (i == takeIndex) { items[takeIndex] = null; @@ -167,69 +200,82 @@ } /** - * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and default access policy. * * @param capacity the capacity of this queue - * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1 + * @throws IllegalArgumentException if {@code capacity < 1} */ public ArrayBlockingQueue(int capacity) { this(capacity, false); } /** - * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and the specified access policy. * * @param capacity the capacity of this queue - * @param fair if <tt>true</tt> then queue accesses for threads blocked + * @param fair if {@code true} then queue accesses for threads blocked * on insertion or removal, are processed in FIFO order; - * if <tt>false</tt> the access order is unspecified. - * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1 + * if {@code false} the access order is unspecified. + * @throws IllegalArgumentException if {@code capacity < 1} */ public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); - this.items = (E[]) new Object[capacity]; + this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } /** - * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed) + * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity, the specified access policy and initially containing the * elements of the given collection, * added in traversal order of the collection's iterator. * * @param capacity the capacity of this queue - * @param fair if <tt>true</tt> then queue accesses for threads blocked + * @param fair if {@code true} then queue accesses for threads blocked * on insertion or removal, are processed in FIFO order; - * if <tt>false</tt> the access order is unspecified. + * if {@code false} the access order is unspecified. * @param c the collection of elements to initially contain - * @throws IllegalArgumentException if <tt>capacity</tt> is less than - * <tt>c.size()</tt>, or less than 1. + * @throws IllegalArgumentException if {@code capacity} is less than + * {@code c.size()}, or less than 1. * @throws NullPointerException if the specified collection or any * of its elements are null */ public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { this(capacity, fair); - if (capacity < c.size()) - throw new IllegalArgumentException(); - for (E e : c) - add(e); + final ReentrantLock lock = this.lock; + lock.lock(); // Lock only for visibility, not mutual exclusion + try { + int i = 0; + try { + for (E e : c) { + checkNotNull(e); + items[i++] = e; + } + } catch (ArrayIndexOutOfBoundsException ex) { + throw new IllegalArgumentException(); + } + count = i; + putIndex = (i == capacity) ? 0 : i; + } finally { + lock.unlock(); + } } /** * Inserts the specified element at the tail of this queue if it is * possible to do so immediately without exceeding the queue's capacity, - * returning <tt>true</tt> upon success and throwing an - * <tt>IllegalStateException</tt> if this queue is full. + * returning {@code true} upon success and throwing an + * {@code IllegalStateException} if this queue is full. * * @param e the element to add - * @return <tt>true</tt> (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws IllegalStateException if this queue is full * @throws NullPointerException if the specified element is null */ @@ -240,14 +286,14 @@ /** * Inserts the specified element at the tail of this queue if it is * possible to do so immediately without exceeding the queue's capacity, - * returning <tt>true</tt> upon success and <tt>false</tt> if this queue + * returning {@code true} upon success and {@code false} if this queue * is full. This method is generally preferable to method {@link #add}, * which can fail to insert an element only by throwing an exception. * * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { - if (e == null) throw new NullPointerException(); + checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { @@ -270,18 +316,12 @@ * @throws NullPointerException {@inheritDoc} */ public void put(E e) throws InterruptedException { - if (e == null) throw new NullPointerException(); - final E[] items = this.items; + checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - try { - while (count == items.length) - notFull.await(); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } + while (count == items.length) + notFull.await(); insert(e); } finally { lock.unlock(); @@ -299,25 +339,18 @@ public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (e == null) throw new NullPointerException(); + checkNotNull(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - for (;;) { - if (count != items.length) { - insert(e); - return true; - } + while (count == items.length) { if (nanos <= 0) return false; - try { - nanos = notFull.awaitNanos(nanos); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } + nanos = notFull.awaitNanos(nanos); } + insert(e); + return true; } finally { lock.unlock(); } @@ -327,10 +360,7 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - if (count == 0) - return null; - E x = extract(); - return x; + return (count == 0) ? null : extract(); } finally { lock.unlock(); } @@ -340,15 +370,9 @@ final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - try { - while (count == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = extract(); - return x; + while (count == 0) + notEmpty.await(); + return extract(); } finally { lock.unlock(); } @@ -359,21 +383,12 @@ final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - for (;;) { - if (count != 0) { - E x = extract(); - return x; - } + while (count == 0) { if (nanos <= 0) return null; - try { - nanos = notEmpty.awaitNanos(nanos); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - + nanos = notEmpty.awaitNanos(nanos); } + return extract(); } finally { lock.unlock(); } @@ -383,7 +398,7 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - return (count == 0) ? null : items[takeIndex]; + return (count == 0) ? null : itemAt(takeIndex); } finally { lock.unlock(); } @@ -412,10 +427,10 @@ * Returns the number of additional elements that this queue can ideally * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this queue - * less the current <tt>size</tt> of this queue. + * less the current {@code size} of this queue. * * <p>Note that you <em>cannot</em> always tell if an attempt to insert - * an element will succeed by inspecting <tt>remainingCapacity</tt> + * an element will succeed by inspecting {@code remainingCapacity} * because it may be the case that another thread is about to * insert or remove an element. */ @@ -431,59 +446,56 @@ /** * Removes a single instance of the specified element from this queue, - * if it is present. More formally, removes an element <tt>e</tt> such - * that <tt>o.equals(e)</tt>, if this queue contains one or more such + * 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 <tt>true</tt> if this queue contained the specified element + * Returns {@code true} if this queue contained the specified element * (or equivalently, if this queue changed as a result of the call). * + * <p>Removal of interior elements in circular array based queues + * is an intrinsically slow and disruptive operation, so should + * be undertaken only in exceptional circumstances, ideally + * only when the queue is known not to be accessible by other + * threads. + * * @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 + * @return {@code true} if this queue changed as a result of the call */ public boolean remove(Object o) { if (o == null) return false; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = 0; - for (;;) { - if (k++ >= count) - return false; + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) { if (o.equals(items[i])) { removeAt(i); return true; } - i = inc(i); } - + return false; } finally { lock.unlock(); } } /** - * Returns <tt>true</tt> if this queue contains the specified element. - * More formally, returns <tt>true</tt> if and only if this queue contains - * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>. + * 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 <tt>true</tt> if this queue contains the specified element + * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { if (o == null) return false; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = 0; - while (k++ < count) { + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) if (o.equals(items[i])) return true; - i = inc(i); - } return false; } finally { lock.unlock(); @@ -504,17 +516,14 @@ * @return an array containing all of the elements in this queue */ public Object[] toArray() { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { + final int count = this.count; Object[] a = new Object[count]; - int k = 0; - int i = takeIndex; - while (k < count) { - a[k++] = items[i]; - i = inc(i); - } + for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) + a[k] = items[i]; return a; } finally { lock.unlock(); @@ -531,22 +540,22 @@ * <p>If this queue fits in the specified array with room to spare * (i.e., the array has more elements than this queue), the element in * the array immediately following the end of the queue is set to - * <tt>null</tt>. + * {@code null}. * * <p>Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * - * <p>Suppose <tt>x</tt> is a queue known to contain only strings. + * <p>Suppose {@code x} is a queue known to contain only strings. * The following code can be used to dump the queue into a newly - * allocated array of <tt>String</tt>: + * allocated array of {@code String}: * * <pre> * String[] y = x.toArray(new String[0]);</pre> * - * Note that <tt>toArray(new Object[0])</tt> is identical in function to - * <tt>toArray()</tt>. + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. * * @param a the array into which the elements of the queue are to * be stored, if it is big enough; otherwise, a new array of the @@ -557,24 +566,20 @@ * this queue * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - if (a.length < count) + final int count = this.count; + final int len = a.length; + if (len < count) a = (T[])java.lang.reflect.Array.newInstance( - a.getClass().getComponentType(), - count - ); - - int k = 0; - int i = takeIndex; - while (k < count) { - a[k++] = (T)items[i]; - i = inc(i); - } - if (a.length > count) + a.getClass().getComponentType(), count); + for (int i = takeIndex, k = 0; k < count; i = inc(i), k++) + a[k] = (T) items[i]; + if (len > count) a[count] = null; return a; } finally { @@ -586,7 +591,19 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - return super.toString(); + int k = count; + if (k == 0) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = takeIndex; ; i = inc(i)) { + Object e = items[i]; + sb.append(e == this ? "(this Collection)" : e); + if (--k == 0) + return sb.append(']').toString(); + sb.append(',').append(' '); + } } finally { lock.unlock(); } @@ -597,16 +614,12 @@ * The queue will be empty after this call returns. */ public void clear() { - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int k = count; - while (k-- > 0) { + for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) items[i] = null; - i = inc(i); - } count = 0; putIndex = 0; takeIndex = 0; @@ -623,11 +636,10 @@ * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection<? super E> c) { - if (c == null) - throw new NullPointerException(); + checkNotNull(c); if (c == this) throw new IllegalArgumentException(); - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { @@ -635,7 +647,7 @@ int n = 0; int max = count; while (n < max) { - c.add(items[i]); + c.add(this.<E>cast(items[i])); items[i] = null; i = inc(i); ++n; @@ -659,22 +671,20 @@ * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection<? super E> c, int maxElements) { - if (c == null) - throw new NullPointerException(); + checkNotNull(c); if (c == this) throw new IllegalArgumentException(); if (maxElements <= 0) return 0; - final E[] items = this.items; + final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { int i = takeIndex; int n = 0; - int sz = count; - int max = (maxElements < count)? maxElements : count; + int max = (maxElements < count) ? maxElements : count; while (n < max) { - c.add(items[i]); + c.add(this.<E>cast(items[i])); items[i] = null; i = inc(i); ++n; @@ -690,11 +700,13 @@ } } - /** * Returns an iterator over the elements in this queue in proper sequence. - * The returned <tt>Iterator</tt> is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, + * The elements will be returned in order from first (head) to last (tail). + * + * <p>The returned {@code 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. @@ -702,83 +714,65 @@ * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(); - } finally { - lock.unlock(); - } + return new Itr(); } /** - * Iterator for ArrayBlockingQueue + * 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. */ private class Itr implements Iterator<E> { - /** - * Index of element to be returned by next, - * or a negative number if no such. - */ - private int nextIndex; - - /** - * nextItem holds on to item fields because once we claim - * that an element exists in hasNext(), we must return it in - * the following next() call even if it was in the process of - * being removed when hasNext() was called. - */ - private E nextItem; - - /** - * Index of element returned by most recent call to next. - * Reset to -1 if this element is deleted by a call to remove. - */ - private int lastRet; + 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 Itr() { - lastRet = -1; - if (count == 0) - nextIndex = -1; - else { - nextIndex = takeIndex; - nextItem = items[takeIndex]; + final ReentrantLock lock = ArrayBlockingQueue.this.lock; + lock.lock(); + try { + lastRet = -1; + if ((remaining = count) > 0) + nextItem = itemAt(nextIndex = takeIndex); + } finally { + lock.unlock(); } } public boolean hasNext() { - /* - * No sync. We can return true by mistake here - * only if this iterator passed across threads, - * which we don't support anyway. - */ - return nextIndex >= 0; - } - - /** - * Checks whether nextIndex is valid; if so setting nextItem. - * Stops iterator when either hits putIndex or sees null item. - */ - private void checkNext() { - if (nextIndex == putIndex) { - nextIndex = -1; - nextItem = null; - } else { - nextItem = items[nextIndex]; - if (nextItem == null) - nextIndex = -1; - } + return remaining > 0; } public E next() { final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - if (nextIndex < 0) + if (remaining <= 0) throw new NoSuchElementException(); lastRet = nextIndex; - E x = nextItem; - nextIndex = inc(nextIndex); - checkNext(); + 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 + } + else + lastItem = x; + while (--remaining > 0 && // skip over nulls + (nextItem = itemAt(nextIndex = inc(nextIndex))) == null) + ; return x; } finally { lock.unlock(); @@ -793,15 +787,19 @@ if (i == -1) throw new IllegalStateException(); lastRet = -1; - - int ti = takeIndex; - removeAt(i); - // back up cursor (reset to front if was first element) - nextIndex = (i == ti) ? takeIndex : i; - checkNext(); + 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); + } } finally { lock.unlock(); } } } + }
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Feb 09 09:32:04 2011 -0500 @@ -869,6 +869,8 @@ /** * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. * * @throws NullPointerException if the specified element is null */ @@ -878,6 +880,8 @@ /** * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. * * <p>This method is equivalent to {@link #add}. * @@ -889,8 +893,9 @@ /** * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never return {@code false}. * - * @return {@code true} always + * @return {@code true} (as specified by {@link Deque#offerFirst}) * @throws NullPointerException if the specified element is null */ public boolean offerFirst(E e) { @@ -900,10 +905,11 @@ /** * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never return {@code false}. * * <p>This method is equivalent to {@link #add}. * - * @return {@code true} always + * @return {@code true} (as specified by {@link Deque#offerLast}) * @throws NullPointerException if the specified element is null */ public boolean offerLast(E e) { @@ -983,6 +989,7 @@ /** * Inserts the specified element at the tail of this deque. + * As the deque 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 @@ -993,6 +1000,8 @@ /** * Inserts the specified element at the tail of this deque. + * As the deque 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
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Feb 09 09:32:04 2011 -0500 @@ -269,6 +269,8 @@ /** * 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 @@ -298,6 +300,7 @@ /** * 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
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Feb 09 09:32:04 2011 -0500 @@ -374,17 +374,11 @@ null, null, 1); } - /** Updater for casHead */ - private static final - AtomicReferenceFieldUpdater<ConcurrentSkipListMap, HeadIndex> - headUpdater = AtomicReferenceFieldUpdater.newUpdater - (ConcurrentSkipListMap.class, HeadIndex.class, "head"); - /** * compareAndSet head node */ private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) { - return headUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); } /* ---------------- Nodes -------------- */ @@ -423,28 +417,18 @@ this.next = next; } - /** Updater for casNext */ - static final AtomicReferenceFieldUpdater<Node, Node> - nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (Node.class, Node.class, "next"); - - /** Updater for casValue */ - static final AtomicReferenceFieldUpdater<Node, Object> - valueUpdater = AtomicReferenceFieldUpdater.newUpdater - (Node.class, Object.class, "value"); - /** * compareAndSet value field */ boolean casValue(Object cmp, Object val) { - return valueUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val); } /** * compareAndSet next field */ boolean casNext(Node<K,V> cmp, Node<K,V> val) { - return nextUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } /** @@ -522,6 +506,14 @@ return null; 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); + } /* ---------------- Indexing -------------- */ @@ -547,16 +539,11 @@ this.right = right; } - /** Updater for casRight */ - static final AtomicReferenceFieldUpdater<Index, Index> - rightUpdater = AtomicReferenceFieldUpdater.newUpdater - (Index.class, Index.class, "right"); - /** * compareAndSet right field */ final boolean casRight(Index<K,V> cmp, Index<K,V> val) { - return rightUpdater.compareAndSet(this, cmp, val); + return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val); } /** @@ -591,6 +578,12 @@ final boolean unlink(Index<K,V> succ) { return !indexesDeletedNode() && casRight(succ, succ.right); } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long rightOffset = + objectFieldOffset(UNSAFE, "right", Index.class); + } /* ---------------- Head nodes -------------- */ @@ -640,7 +633,8 @@ * cast key as Comparable, which may cause ClassCastException, * which is propagated back to caller. */ - private Comparable<? super K> comparable(Object key) throws ClassCastException { + private Comparable<? super K> comparable(Object key) + throws ClassCastException { if (key == null) throw new NullPointerException(); if (comparator != null) @@ -799,68 +793,12 @@ } /** - * Specialized variant of findNode to perform Map.get. Does a weak - * traversal, not bothering to fix any deleted index nodes, - * returning early if it happens to see key in index, and passing - * over any deleted base nodes, falling back to getUsingFindNode - * only if it would otherwise return value from an ongoing - * deletion. Also uses "bound" to eliminate need for some - * comparisons (see Pugh Cookbook). Also folds uses of null checks - * and node-skipping because markers have null keys. + * Gets value for key using findNode. * @param okey the key * @return the value, or null if absent */ private V doGet(Object okey) { Comparable<? super K> key = comparable(okey); - Node<K,V> bound = null; - Index<K,V> q = head; - Index<K,V> r = q.right; - Node<K,V> n; - K k; - int c; - for (;;) { - Index<K,V> d; - // Traverse rights - if (r != null && (n = r.node) != bound && (k = n.key) != null) { - if ((c = key.compareTo(k)) > 0) { - q = r; - r = r.right; - continue; - } else if (c == 0) { - Object v = n.value; - return (v != null)? (V)v : getUsingFindNode(key); - } else - bound = n; - } - - // Traverse down - if ((d = q.down) != null) { - q = d; - r = d.right; - } else - break; - } - - // Traverse nexts - for (n = q.node.next; n != null; n = n.next) { - if ((k = n.key) != null) { - if ((c = key.compareTo(k)) == 0) { - Object v = n.value; - return (v != null)? (V)v : getUsingFindNode(key); - } else if (c < 0) - break; - } - } - return null; - } - - /** - * Performs map.get via findNode. Used as a backup if doGet - * encounters an in-progress deletion. - * @param key the key - * @return the value, or null if absent - */ - private V getUsingFindNode(Comparable<? super K> key) { /* * Loop needed here and elsewhere in case value field goes * null just as it is about to be returned, in which case we @@ -943,7 +881,7 @@ x ^= x << 13; x ^= x >>> 17; randomSeed = x ^= x << 5; - if ((x & 0x8001) != 0) // test highest and lowest bits + if ((x & 0x80000001) != 0) // test highest and lowest bits return 0; int level = 1; while (((x >>>= 1) & 1) != 0) ++level; @@ -1256,7 +1194,7 @@ Node<K,V> n = b.next; for (;;) { if (n == null) - return (b.isBaseHeader())? null : b; + return b.isBaseHeader() ? null : b; Node<K,V> f = n.next; // inconsistent read if (n != b.next) break; @@ -1374,7 +1312,7 @@ Node<K,V> n = b.next; for (;;) { if (n == null) - return ((rel & LT) == 0 || b.isBaseHeader())? null : b; + return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b; Node<K,V> f = n.next; if (n != b.next) // inconsistent read break; @@ -1390,7 +1328,7 @@ (c < 0 && (rel & LT) == 0)) return n; if ( c <= 0 && (rel & LT) != 0) - return (b.isBaseHeader())? null : b; + return b.isBaseHeader() ? null : b; b = n; n = f; } @@ -1744,7 +1682,7 @@ if (n.getValidValue() != null) ++count; } - return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count; + return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count; } /** @@ -2099,7 +2037,7 @@ */ public K lowerKey(K key) { Node<K,V> n = findNear(key, LT); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2123,7 +2061,7 @@ */ public K floorKey(K key) { Node<K,V> n = findNear(key, LT|EQ); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2145,7 +2083,7 @@ */ public K ceilingKey(K key) { Node<K,V> n = findNear(key, GT|EQ); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2169,7 +2107,7 @@ */ public K higherKey(K key) { Node<K,V> n = findNear(key, GT); - return (n == null)? null : n.key; + return (n == null) ? null : n.key; } /** @@ -2342,7 +2280,8 @@ return list; } - static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> { + static final class KeySet<E> + extends AbstractSet<E> implements NavigableSet<E> { private final ConcurrentNavigableMap<E,Object> m; KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; } public int size() { return m.size(); } @@ -2359,11 +2298,11 @@ public E last() { return m.lastKey(); } public E pollFirst() { Map.Entry<E,Object> e = m.pollFirstEntry(); - return e == null? null : e.getKey(); + return (e == null) ? null : e.getKey(); } public E pollLast() { Map.Entry<E,Object> e = m.pollLastEntry(); - return e == null? null : e.getKey(); + return (e == null) ? null : e.getKey(); } public Iterator<E> iterator() { if (m instanceof ConcurrentSkipListMap) @@ -2710,9 +2649,9 @@ rel &= ~m.LT; } if (tooLow(key)) - return ((rel & m.LT) != 0)? null : lowestEntry(); + return ((rel & m.LT) != 0) ? null : lowestEntry(); if (tooHigh(key)) - return ((rel & m.LT) != 0)? highestEntry() : null; + return ((rel & m.LT) != 0) ? highestEntry() : null; for (;;) { Node<K,V> n = m.findNear(key, rel); if (n == null || !inBounds(n.key)) @@ -2783,7 +2722,7 @@ public V remove(Object key) { K k = (K)key; - return (!inBounds(k))? null : m.remove(k); + return (!inBounds(k)) ? null : m.remove(k); } public int size() { @@ -2794,7 +2733,7 @@ if (n.getValidValue() != null) ++count; } - return count >= Integer.MAX_VALUE? Integer.MAX_VALUE : (int)count; + return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count; } public boolean isEmpty() { @@ -2972,27 +2911,27 @@ } public K firstKey() { - return isDescending? highestKey() : lowestKey(); + return isDescending ? highestKey() : lowestKey(); } public K lastKey() { - return isDescending? lowestKey() : highestKey(); + return isDescending ? lowestKey() : highestKey(); } public Map.Entry<K,V> firstEntry() { - return isDescending? highestEntry() : lowestEntry(); + return isDescending ? highestEntry() : lowestEntry(); } public Map.Entry<K,V> lastEntry() { - return isDescending? lowestEntry() : highestEntry(); + return isDescending ? lowestEntry() : highestEntry(); } public Map.Entry<K,V> pollFirstEntry() { - return isDescending? removeHighest() : removeLowest(); + return isDescending ? removeHighest() : removeLowest(); } public Map.Entry<K,V> pollLastEntry() { - return isDescending? removeLowest() : removeHighest(); + return isDescending ? removeLowest() : removeHighest(); } /* ---------------- Submap Views -------------- */ @@ -3141,4 +3080,22 @@ } } } + + // 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) { + 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; + } + } + }
--- a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Feb 09 09:32:04 2011 -0500 @@ -832,7 +832,7 @@ } /** - * Save the state of the list to a stream (i.e., serialize it). + * Saves the state of the list to a stream (that is, serializes it). * * @serialData The length of the array backing the list is emitted * (int), followed by all of its elements (each an Object) @@ -842,27 +842,25 @@ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ - // Write out element count, and any hidden stuff s.defaultWriteObject(); Object[] elements = getArray(); - int len = elements.length; // Write out array length - s.writeInt(len); + s.writeInt(elements.length); // Write out all elements in the proper order. - for (int i = 0; i < len; i++) - s.writeObject(elements[i]); + for (Object element : elements) + s.writeObject(element); } /** - * Reconstitute the list from a stream (i.e., deserialize it). + * Reconstitutes the list from a stream (that is, deserializes it). + * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - // Read in size, and any hidden stuff s.defaultReadObject(); // bind to new lock
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Wed Feb 09 09:32:04 2011 -0500 @@ -525,8 +525,8 @@ */ private volatile long eventWaiters; - private static final int EVENT_COUNT_SHIFT = 32; - private static final long WAITER_ID_MASK = (1L << 16) - 1L; + private static final int EVENT_COUNT_SHIFT = 32; + private static final int WAITER_ID_MASK = (1 << 16) - 1; /** * A counter for events that may wake up worker threads: @@ -615,7 +615,7 @@ // are usually manually inlined by callers /** - * Increments running count part of workerCounts + * Increments running count part of workerCounts. */ final void incrementRunningCount() { int c; @@ -625,7 +625,17 @@ } /** - * Tries to decrement running count unless already zero + * Tries to increment running count part of workerCounts. + */ + final boolean tryIncrementRunningCount() { + int c; + return UNSAFE.compareAndSwapInt(this, workerCountsOffset, + c = workerCounts, + c + ONE_RUNNING); + } + + /** + * Tries to decrement running count unless already zero. */ final boolean tryDecrementRunningCount() { int wc = workerCounts; @@ -698,10 +708,11 @@ for (k = 0; k < n && ws[k] != null; ++k) ; if (k == n) - ws = Arrays.copyOf(ws, n << 1); + ws = workers = Arrays.copyOf(ws, n << 1); } ws[k] = w; - workers = ws; // volatile array write ensures slot visibility + int c = eventCount; // advance event count to ensure visibility + UNSAFE.compareAndSwapInt(this, eventCountOffset, c, c+1); } finally { lock.unlock(); } @@ -734,7 +745,7 @@ */ final void workerTerminated(ForkJoinWorkerThread w) { forgetWorker(w); - decrementWorkerCounts(w.isTrimmed()? 0 : ONE_RUNNING, ONE_TOTAL); + decrementWorkerCounts(w.isTrimmed() ? 0 : ONE_RUNNING, ONE_TOTAL); while (w.stealCount != 0) // collect final count tryAccumulateStealCount(w); tryTerminate(false); @@ -746,24 +757,23 @@ * Releases workers blocked on a count not equal to current count. * Normally called after precheck that eventWaiters isn't zero to * avoid wasted array checks. Gives up upon a change in count or - * upon releasing two workers, letting others take over. + * upon releasing four workers, letting others take over. */ private void releaseEventWaiters() { ForkJoinWorkerThread[] ws = workers; int n = ws.length; long h = eventWaiters; int ec = eventCount; - boolean releasedOne = false; + int releases = 4; ForkJoinWorkerThread w; int id; - while ((id = ((int)(h & WAITER_ID_MASK)) - 1) >= 0 && + while ((id = (((int)h) & WAITER_ID_MASK) - 1) >= 0 && (int)(h >>> EVENT_COUNT_SHIFT) != ec && id < n && (w = ws[id]) != null) { if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, h, w.nextWaiter)) { LockSupport.unpark(w); - if (releasedOne) // exit on second release + if (--releases == 0) break; - releasedOne = true; } if (eventCount != ec) break; @@ -793,7 +803,7 @@ long nh = (((long)ec) << EVENT_COUNT_SHIFT) | ((long)(w.poolIndex+1)); long h; while ((runState < SHUTDOWN || !tryTerminate(false)) && - (((int)((h = eventWaiters) & WAITER_ID_MASK)) == 0 || + (((int)(h = eventWaiters) & WAITER_ID_MASK) == 0 || (int)(h >>> EVENT_COUNT_SHIFT) == ec) && eventCount == ec) { if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, @@ -820,9 +830,9 @@ if (tryAccumulateStealCount(w)) { // transfer while idle boolean untimed = (w.nextWaiter != 0L || (workerCounts & RUNNING_COUNT_MASK) <= 1); - long startTime = untimed? 0 : System.nanoTime(); + long startTime = untimed ? 0 : System.nanoTime(); Thread.interrupted(); // clear/ignore interrupt - if (eventCount != ec || w.isTerminating()) + if (w.isTerminating() || eventCount != ec) break; // recheck after clear if (untimed) LockSupport.park(w); @@ -860,7 +870,8 @@ if ((sw = spareWaiters) != 0 && (id = (sw & SPARE_ID_MASK) - 1) >= 0 && id < n && (w = ws[id]) != null && - (workerCounts & RUNNING_COUNT_MASK) < parallelism && + (runState >= TERMINATING || + (workerCounts & RUNNING_COUNT_MASK) < parallelism) && spareWaiters == sw && UNSAFE.compareAndSwapInt(this, spareWaitersOffset, sw, w.nextSpare)) { @@ -914,12 +925,8 @@ break; } w.start(recordWorker(w), ueh); - if ((workerCounts >>> TOTAL_COUNT_SHIFT) >= pc) { - int c; // advance event count - UNSAFE.compareAndSwapInt(this, eventCountOffset, - c = eventCount, c+1); + if ((workerCounts >>> TOTAL_COUNT_SHIFT) >= pc) break; // add at most one unless total below target - } } } if (eventWaiters != 0L) @@ -955,7 +962,7 @@ } else if ((h = eventWaiters) != 0L) { long nh; - int id = ((int)(h & WAITER_ID_MASK)) - 1; + int id = (((int)h) & WAITER_ID_MASK) - 1; if (id >= 0 && id < n && (w = ws[id]) != null && (nh = w.nextWaiter) != 0L && // keep at least one worker UNSAFE.compareAndSwapLong(this, eventWaitersOffset, h, nh)) @@ -1003,24 +1010,31 @@ int pc = parallelism; while (w.runState == 0) { int rs = runState; - if (rs >= TERMINATING) { // propagate shutdown + if (rs >= TERMINATING) { // propagate shutdown w.shutdown(); break; } if ((inactivate || (active && (rs & ACTIVE_COUNT_MASK) >= pc)) && - UNSAFE.compareAndSwapInt(this, runStateOffset, rs, rs - 1)) + UNSAFE.compareAndSwapInt(this, runStateOffset, rs, --rs)) { inactivate = active = w.active = false; - int wc = workerCounts; + if (rs == SHUTDOWN) { // all inactive and shut down + tryTerminate(false); + continue; + } + } + int wc = workerCounts; // try to suspend as spare if ((wc & RUNNING_COUNT_MASK) > pc) { if (!(inactivate |= active) && // must inactivate to suspend - workerCounts == wc && // try to suspend as spare + workerCounts == wc && UNSAFE.compareAndSwapInt(this, workerCountsOffset, wc, wc - ONE_RUNNING)) w.suspendAsSpare(); } else if ((wc >>> TOTAL_COUNT_SHIFT) < pc) helpMaintainParallelism(); // not enough workers - else if (!ran) { + else if (ran) + break; + else { long h = eventWaiters; int ec = eventCount; if (h != 0L && (int)(h >>> EVENT_COUNT_SHIFT) != ec) @@ -1032,8 +1046,6 @@ else if (!(inactivate |= active)) eventSync(w, wec); // must inactivate before sync } - else - break; } } @@ -1043,35 +1055,67 @@ * * @param joinMe the task to join * @param worker the current worker thread + * @param timed true if wait should time out + * @param nanos timeout value if timed */ - final void awaitJoin(ForkJoinTask<?> joinMe, ForkJoinWorkerThread worker) { + final void awaitJoin(ForkJoinTask<?> joinMe, ForkJoinWorkerThread worker, + boolean timed, long nanos) { + long startTime = timed ? System.nanoTime() : 0L; int retries = 2 + (parallelism >> 2); // #helpJoins before blocking + boolean running = true; // false when count decremented while (joinMe.status >= 0) { - int wc; - worker.helpJoinTask(joinMe); + if (runState >= TERMINATING) { + joinMe.cancelIgnoringExceptions(); + break; + } + running = worker.helpJoinTask(joinMe, running); if (joinMe.status < 0) break; - else if (retries > 0) + if (retries > 0) { --retries; - else if (((wc = workerCounts) & RUNNING_COUNT_MASK) != 0 && - UNSAFE.compareAndSwapInt(this, workerCountsOffset, - wc, wc - ONE_RUNNING)) { - int stat, c; long h; - while ((stat = joinMe.status) >= 0 && - (h = eventWaiters) != 0L && // help release others - (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) + continue; + } + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) != 0) { + if (running) { + if (!UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) + continue; + running = false; + } + long h = eventWaiters; + if (h != 0L && (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) releaseEventWaiters(); - if (stat >= 0 && - ((workerCounts & RUNNING_COUNT_MASK) == 0 || - (stat = - joinMe.internalAwaitDone(JOIN_TIMEOUT_MILLIS)) >= 0)) - helpMaintainParallelism(); // timeout or no running workers - do {} while (!UNSAFE.compareAndSwapInt - (this, workerCountsOffset, - c = workerCounts, c + ONE_RUNNING)); - if (stat < 0) - break; // else restart + if ((workerCounts & RUNNING_COUNT_MASK) != 0) { + long ms; int ns; + if (!timed) { + ms = JOIN_TIMEOUT_MILLIS; + ns = 0; + } + else { // at most JOIN_TIMEOUT_MILLIS per wait + long nt = nanos - (System.nanoTime() - startTime); + if (nt <= 0L) + break; + ms = nt / 1000000; + if (ms > JOIN_TIMEOUT_MILLIS) { + ms = JOIN_TIMEOUT_MILLIS; + ns = 0; + } + else + ns = (int) (nt % 1000000); + } + joinMe.internalAwaitDone(ms, ns); + } + if (joinMe.status < 0) + break; } + helpMaintainParallelism(); + } + if (!running) { + int c; + do {} while (!UNSAFE.compareAndSwapInt + (this, workerCountsOffset, + c = workerCounts, c + ONE_RUNNING)); } } @@ -1082,9 +1126,10 @@ throws InterruptedException { while (!blocker.isReleasable()) { int wc = workerCounts; - if ((wc & RUNNING_COUNT_MASK) != 0 && - UNSAFE.compareAndSwapInt(this, workerCountsOffset, - wc, wc - ONE_RUNNING)) { + if ((wc & RUNNING_COUNT_MASK) == 0) + helpMaintainParallelism(); + else if (UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) { try { while (!blocker.isReleasable()) { long h = eventWaiters; @@ -1129,12 +1174,11 @@ // Finish now if all threads terminated; else in some subsequent call if ((workerCounts >>> TOTAL_COUNT_SHIFT) == 0) { advanceRunLevel(TERMINATED); - termination.arrive(); + termination.forceTermination(); } return true; } - /** * Actions on transition to TERMINATING * @@ -1325,17 +1369,13 @@ // Execution methods /** - * Common code for execute, invoke and submit + * Submits task and creates, starts, or resumes some workers if necessary */ private <T> void doSubmit(ForkJoinTask<T> task) { - if (task == null) - throw new NullPointerException(); - if (runState >= SHUTDOWN) - throw new RejectedExecutionException(); submissionQueue.offer(task); int c; // try to increment event count -- CAS failure OK UNSAFE.compareAndSwapInt(this, eventCountOffset, c = eventCount, c+1); - helpMaintainParallelism(); // create, start, or resume some workers + helpMaintainParallelism(); } /** @@ -1348,8 +1388,33 @@ * scheduled for execution */ public <T> T invoke(ForkJoinTask<T> task) { - doSubmit(task); - return task.join(); + if (task == null) + throw new NullPointerException(); + if (runState >= SHUTDOWN) + throw new RejectedExecutionException(); + Thread t = Thread.currentThread(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)t).pool == this) + return task.invoke(); // bypass submit if in same pool + else { + doSubmit(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) { + if (runState >= SHUTDOWN) + throw new RejectedExecutionException(); + Thread t = Thread.currentThread(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)t).pool == this) + task.fork(); + else + doSubmit(task); } /** @@ -1361,7 +1426,9 @@ * scheduled for execution */ public void execute(ForkJoinTask<?> task) { - doSubmit(task); + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); } // AbstractExecutorService methods @@ -1372,12 +1439,14 @@ * 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); - doSubmit(job); + forkOrSubmit(job); } /** @@ -1390,7 +1459,9 @@ * scheduled for execution */ public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) { - doSubmit(task); + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); return task; } @@ -1400,8 +1471,10 @@ * scheduled for execution */ public <T> ForkJoinTask<T> submit(Callable<T> task) { + if (task == null) + throw new NullPointerException(); ForkJoinTask<T> job = ForkJoinTask.adapt(task); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1411,8 +1484,10 @@ * 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); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1422,12 +1497,14 @@ * 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); - doSubmit(job); + forkOrSubmit(job); return job; } @@ -1725,8 +1802,11 @@ * 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, causing this executor not - * to properly terminate. + * 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 */ @@ -1764,10 +1844,11 @@ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { try { - return termination.awaitAdvanceInterruptibly(0, timeout, unit) > 0; + termination.awaitAdvanceInterruptibly(0, timeout, unit); } catch (TimeoutException ex) { return false; } + return true; } /**
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Wed Feb 09 09:32:04 2011 -0500 @@ -42,6 +42,16 @@ import java.util.RandomAccess; import java.util.Map; import java.util.WeakHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +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.TimeoutException; /** * Abstract base class for tasks that run within a {@link ForkJoinPool}. @@ -129,6 +139,16 @@ * 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 @@ -143,9 +163,10 @@ * 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. If tasks are too big, then parallelism cannot - * improve throughput. If too small, then memory and internal task - * maintenance overhead may overwhelm processing. + * 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 @@ -242,17 +263,20 @@ } /** - * Blocks a worker thread until completion. Called only by - * pool. Currently unused -- pool-based waits use timeout - * version below. + * Blocks a worker thread until completed or timed out. Called + * only by pool. */ - final void internalAwaitDone() { - int s; // the odd construction reduces lock bias effects - while ((s = status) >= 0) { - try { + final void internalAwaitDone(long millis, int nanos) { + int s = status; + if ((s == 0 && + UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL)) || + s > 0) { + try { // the odd construction reduces lock bias effects synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) - wait(); + if (status > 0) + wait(millis, nanos); + else + notifyAll(); } } catch (InterruptedException ie) { cancelIfTerminating(); @@ -261,46 +285,61 @@ } /** - * Blocks a worker thread until completed or timed out. Called - * only by pool. - * - * @return status on exit - */ - final int internalAwaitDone(long millis) { - int s; - if ((s = status) >= 0) { - try { - synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) - wait(millis, 0); - } - } catch (InterruptedException ie) { - cancelIfTerminating(); - } - s = status; - } - return s; - } - - /** * Blocks a non-worker-thread until completion. */ private void externalAwaitDone() { - int s; - while ((s = status) >= 0) { + if (status >= 0) { + boolean interrupted = false; synchronized (this) { - if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)){ - boolean interrupted = false; - while (status >= 0) { + for (;;) { + int s = status; + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else if (s < 0) { + notifyAll(); + break; + } + else { try { wait(); } catch (InterruptedException ie) { interrupted = true; } } - if (interrupted) - Thread.currentThread().interrupt(); - break; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } + } + + /** + * Blocks a non-worker-thread until completion or interruption or timeout. + */ + private void externalInterruptibleAwaitDone(boolean timed, long nanos) + throws InterruptedException { + if (Thread.interrupted()) + throw new InterruptedException(); + if (status >= 0) { + long startTime = timed ? System.nanoTime() : 0L; + synchronized (this) { + for (;;) { + long nt; + int s = status; + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else if (s < 0) { + notifyAll(); + break; + } + else if (!timed) + wait(); + else if ((nt = nanos - (System.nanoTime()-startTime)) > 0L) + wait(nt / 1000000, (int)(nt % 1000000)); + else + break; } } } @@ -335,7 +374,7 @@ * #isDone} returning {@code true}. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -349,10 +388,13 @@ } /** - * Returns the result of the computation when it {@link #isDone is done}. - * This method differs from {@link #get()} in that + * 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}. + * {@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 */ @@ -394,7 +436,7 @@ * unprocessed. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -422,7 +464,7 @@ * normally or exceptionally, or left unprocessed. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -477,7 +519,7 @@ * unprocessed. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -529,25 +571,28 @@ /** * Attempts to cancel execution of this task. This attempt will - * fail if the task has already completed, has already been - * cancelled, or could not be cancelled for some other reason. If - * successful, and this task has not started when cancel is - * called, execution of this task is suppressed, {@link - * #isCancelled} will report true, and {@link #join} will result - * in a {@code CancellationException} being thrown. + * 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 minimal properties hold. In particular, - * the {@code cancel} method itself must not throw exceptions. + * 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 is ignored in the - * default implementation because tasks are not - * cancelled via interruption + * @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 */ @@ -681,23 +726,13 @@ * member of a ForkJoinPool and was interrupted while waiting */ public final V get() throws InterruptedException, ExecutionException { - int s; - if (Thread.currentThread() instanceof ForkJoinWorkerThread) { + Thread t = Thread.currentThread(); + if (t instanceof ForkJoinWorkerThread) quietlyJoin(); - s = status; - } - else { - while ((s = status) >= 0) { - synchronized (this) { // interruptible form of awaitDone - if (UNSAFE.compareAndSwapInt(this, statusOffset, - s, SIGNAL)) { - while (status >= 0) - wait(); - } - } - } - } - if (s < NORMAL) { + else + externalInterruptibleAwaitDone(false, 0L); + int s = status; + if (s != NORMAL) { Throwable ex; if (s == CANCELLED) throw new CancellationException(); @@ -723,72 +758,18 @@ */ public final V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + long nanos = unit.toNanos(timeout); Thread t = Thread.currentThread(); - ForkJoinPool pool; - if (t instanceof ForkJoinWorkerThread) { - ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; - if (status >= 0 && w.unpushTask(this)) - quietlyExec(); - pool = w.pool; - } + if (t instanceof ForkJoinWorkerThread) + ((ForkJoinWorkerThread)t).joinTask(this, true, nanos); else - pool = null; - /* - * Timed wait loop intermixes cases for FJ (pool != null) and - * non FJ threads. For FJ, decrement pool count but don't try - * for replacement; increment count on completion. For non-FJ, - * deal with interrupts. This is messy, but a little less so - * than is splitting the FJ and nonFJ cases. - */ - boolean interrupted = false; - boolean dec = false; // true if pool count decremented - long nanos = unit.toNanos(timeout); - for (;;) { - if (pool == null && Thread.interrupted()) { - interrupted = true; - break; - } - int s = status; - if (s < 0) - break; - if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)) { - long startTime = System.nanoTime(); - long nt; // wait time - while (status >= 0 && - (nt = nanos - (System.nanoTime() - startTime)) > 0) { - if (pool != null && !dec) - dec = pool.tryDecrementRunningCount(); - else { - long ms = nt / 1000000; - int ns = (int) (nt % 1000000); - try { - synchronized (this) { - if (status >= 0) - wait(ms, ns); - } - } catch (InterruptedException ie) { - if (pool != null) - cancelIfTerminating(); - else { - interrupted = true; - break; - } - } - } - } - break; - } - } - if (pool != null && dec) - pool.incrementRunningCount(); - if (interrupted) - throw new InterruptedException(); - int es = status; - if (es != NORMAL) { + externalInterruptibleAwaitDone(true, nanos); + int s = status; + if (s != NORMAL) { Throwable ex; - if (es == CANCELLED) + if (s == CANCELLED) throw new CancellationException(); - if (es == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) + if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) throw new ExecutionException(ex); throw new TimeoutException(); } @@ -819,7 +800,7 @@ return; } } - w.joinTask(this); + w.joinTask(this, false, 0L); } } else @@ -855,7 +836,7 @@ * processed. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -874,6 +855,12 @@ * 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) @@ -895,11 +882,12 @@ } /** - * Returns {@code true} if the current thread is executing as a - * ForkJoinPool computation. + * Returns {@code true} if the current thread is a {@link + * ForkJoinWorkerThread} executing as a ForkJoinPool computation. * - * @return {@code true} if the current thread is executing as a - * ForkJoinPool computation, or false otherwise + * @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; @@ -914,7 +902,7 @@ * were not, stolen. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -933,7 +921,7 @@ * fork other tasks. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -956,7 +944,7 @@ * exceeded. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -1014,7 +1002,7 @@ * otherwise. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -1033,7 +1021,7 @@ * be useful otherwise. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}. @@ -1056,7 +1044,7 @@ * otherwise. * * <p>This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method + * 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}.
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Feb 09 09:32:04 2011 -0500 @@ -38,16 +38,18 @@ import java.util.Random; import java.util.Collection; import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.RejectedExecutionException; /** - * A thread managed by a {@link ForkJoinPool}. 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}. + * 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 * @author Doug Lea @@ -376,7 +378,7 @@ /** * 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. + * 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 @@ -384,7 +386,7 @@ */ protected void onStart() { int rs = seedGenerator.nextInt(); - seed = rs == 0? 1 : rs; // seed must be nonzero + seed = (rs == 0) ? 1 : rs; // seed must be nonzero // Allocate name string and arrays in this thread String pid = Integer.toString(pool.getPoolNumber()); @@ -426,7 +428,7 @@ /** * This method is required to be public, but should never be * called explicitly. It performs the main run loop to execute - * ForkJoinTasks. + * {@link ForkJoinTask}s. */ public void run() { Throwable exception = null; @@ -628,6 +630,19 @@ if (t == null) // lost to stealer break; if (UNSAFE.compareAndSwapObject(q, u, t, null)) { + /* + * Note: here and in related methods, as a + * performance (not correctness) issue, we'd like + * to encourage compiler not to arbitrarily + * postpone setting sp after successful CAS. + * 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. + */ sp = s; // putOrderedInt may encourage more timely write // UNSAFE.putOrderedInt(this, spOffset, s); return t; @@ -777,10 +792,10 @@ // Run State management // status check methods used mainly by ForkJoinPool - final boolean isRunning() { return runState == 0; } - final boolean isTerminated() { return (runState & TERMINATED) != 0; } - final boolean isSuspended() { return (runState & SUSPENDED) != 0; } - final boolean isTrimmed() { return (runState & TRIMMED) != 0; } + final boolean isRunning() { return runState == 0; } + final boolean isTerminated() { return (runState & TERMINATED) != 0; } + final boolean isSuspended() { return (runState & SUSPENDED) != 0; } + final boolean isTrimmed() { return (runState & TRIMMED) != 0; } final boolean isTerminating() { if ((runState & TERMINATING) != 0) @@ -884,8 +899,7 @@ */ final void cancelTasks() { ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks - if (cj != null) { - currentJoin = null; + if (cj != null && cj.status >= 0) { cj.cancelIgnoringExceptions(); try { this.interrupt(); // awaken wait @@ -893,10 +907,8 @@ } } ForkJoinTask<?> cs = currentSteal; - if (cs != null) { - currentSteal = null; + if (cs != null && cs.status >= 0) cs.cancelIgnoringExceptions(); - } while (base != sp) { ForkJoinTask<?> t = deqTask(); if (t != null) @@ -959,57 +971,23 @@ * Possibly runs some tasks and/or blocks, until task is done. * * @param joinMe the task to join + * @param timed true if use timed wait + * @param nanos wait time if timed */ - final void joinTask(ForkJoinTask<?> joinMe) { + final void joinTask(ForkJoinTask<?> joinMe, boolean timed, long nanos) { // currentJoin only written by this thread; only need ordered store ForkJoinTask<?> prevJoin = currentJoin; UNSAFE.putOrderedObject(this, currentJoinOffset, joinMe); - if (sp != base) - localHelpJoinTask(joinMe); - if (joinMe.status >= 0) - pool.awaitJoin(joinMe, this); + pool.awaitJoin(joinMe, this, timed, nanos); UNSAFE.putOrderedObject(this, currentJoinOffset, prevJoin); } /** - * Run tasks in local queue until given task is done. - * - * @param joinMe the task to join - */ - private void localHelpJoinTask(ForkJoinTask<?> joinMe) { - int s; - ForkJoinTask<?>[] q; - while (joinMe.status >= 0 && (s = sp) != base && (q = queue) != null) { - int i = (q.length - 1) & --s; - long u = (i << qShift) + qBase; // raw offset - ForkJoinTask<?> t = q[i]; - if (t == null) // lost to a stealer - break; - if (UNSAFE.compareAndSwapObject(q, u, t, null)) { - /* - * This recheck (and similarly in helpJoinTask) - * handles cases where joinMe is independently - * cancelled or forced even though there is other work - * available. Back out of the pop by putting t back - * into slot before we commit by writing sp. - */ - if (joinMe.status < 0) { - UNSAFE.putObjectVolatile(q, u, t); - break; - } - sp = s; - // UNSAFE.putOrderedInt(this, spOffset, s); - t.quietlyExec(); - } - } - } - - /** - * Unless terminating, tries to locate and help perform 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. + * Tries to locate and help perform 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, @@ -1019,77 +997,127 @@ * don't work out. * * @param joinMe the task to join + * @param running if false, then must update pool count upon + * running a task + * @return value of running on exit */ - final void helpJoinTask(ForkJoinTask<?> joinMe) { - ForkJoinWorkerThread[] ws; - int n; - if (joinMe.status < 0) // already done - return; - if ((runState & TERMINATING) != 0) { // cancel if shutting down - joinMe.cancelIgnoringExceptions(); - return; + final boolean helpJoinTask(ForkJoinTask<?> joinMe, boolean running) { + /* + * Initial checks to (1) abort if terminating; (2) clean out + * old cancelled tasks from local queue; (3) if joinMe is next + * task, run it; (4) omit scan if local queue nonempty (since + * it may contain non-descendents of joinMe). + */ + ForkJoinPool p = pool; + for (;;) { + ForkJoinTask<?>[] q; + int s; + if (joinMe.status < 0) + return running; + else if ((runState & TERMINATING) != 0) { + joinMe.cancelIgnoringExceptions(); + return running; + } + else if ((s = sp) == base || (q = queue) == null) + break; // queue empty + else { + int i = (q.length - 1) & --s; + long u = (i << qShift) + qBase; // raw offset + ForkJoinTask<?> t = q[i]; + if (t == null) + break; // lost to a stealer + else if (t != joinMe && t.status >= 0) + return running; // cannot safely help + else if ((running || + (running = p.tryIncrementRunningCount())) && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + sp = s; // putOrderedInt may encourage more timely write + // UNSAFE.putOrderedInt(this, spOffset, s); + t.quietlyExec(); + } + } } - if ((ws = pool.workers) == null || (n = ws.length) <= 1) - return; // need at least 2 workers - ForkJoinTask<?> task = joinMe; // base of chain - ForkJoinWorkerThread thread = this; // thread with stolen task - for (int d = 0; d < MAX_HELP_DEPTH; ++d) { // chain length - // Try to find v, the stealer of task, by first using hint - ForkJoinWorkerThread v = ws[thread.stealHint & (n - 1)]; - if (v == null || v.currentSteal != task) { - for (int j = 0; ; ++j) { // search array - if (j < n) { - ForkJoinTask<?> vs; - if ((v = ws[j]) != null && - (vs = v.currentSteal) != null) { - if (joinMe.status < 0 || task.status < 0) - return; // stale or done - if (vs == task) { - thread.stealHint = j; - break; // save hint for next time + int n; // worker array size + ForkJoinWorkerThread[] ws = p.workers; + if (ws != null && (n = ws.length) > 1) { // need at least 2 workers + ForkJoinTask<?> task = joinMe; // base of chain + ForkJoinWorkerThread thread = this; // thread with stolen task + + outer:for (int d = 0; d < MAX_HELP_DEPTH; ++d) { // chain length + // Try to find v, the stealer of task, by first using hint + ForkJoinWorkerThread v = ws[thread.stealHint & (n - 1)]; + if (v == null || v.currentSteal != task) { + for (int j = 0; ; ++j) { // search array + if (j < n) { + ForkJoinTask<?> vs; + if ((v = ws[j]) != null && + (vs = v.currentSteal) != null) { + if (joinMe.status < 0) + break outer; + if (vs == task) { + if (task.status < 0) + break outer; // stale + thread.stealHint = j; + break; // save hint for next time + } } } + else + break outer; // no stealer } - else - return; // no stealer } + + // Try to help v, using specialized form of deqTask + for (;;) { + if (joinMe.status < 0) + break outer; + int b = v.base; + ForkJoinTask<?>[] q = v.queue; + if (b == v.sp || q == null) + break; // empty + int i = (q.length - 1) & b; + long u = (i << qShift) + qBase; + ForkJoinTask<?> t = q[i]; + if (task.status < 0) + break outer; // stale + if (t != null && + (running || + (running = p.tryIncrementRunningCount())) && + v.base == b++ && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + if (t != joinMe && joinMe.status < 0) { + UNSAFE.putObjectVolatile(q, u, t); + break outer; // joinMe cancelled; back out + } + v.base = b; + if (t.status >= 0) { + ForkJoinTask<?> ps = currentSteal; + int pid = poolIndex; + v.stealHint = pid; + UNSAFE.putOrderedObject(this, + currentStealOffset, t); + t.quietlyExec(); + UNSAFE.putOrderedObject(this, + currentStealOffset, ps); + } + } + else if ((runState & TERMINATING) != 0) { + joinMe.cancelIgnoringExceptions(); + break outer; + } + } + + // Try to descend to find v's stealer + ForkJoinTask<?> next = v.currentJoin; + if (task.status < 0 || next == null || next == task || + joinMe.status < 0) + break; // done, stale, dead-end, or cyclic + task = next; + thread = v; } - for (;;) { // Try to help v, using specialized form of deqTask - if (joinMe.status < 0) - return; - int b = v.base; - ForkJoinTask<?>[] q = v.queue; - if (b == v.sp || q == null) - break; - int i = (q.length - 1) & b; - long u = (i << qShift) + qBase; - ForkJoinTask<?> t = q[i]; - int pid = poolIndex; - ForkJoinTask<?> ps = currentSteal; - if (task.status < 0) - return; // stale or done - if (t != null && v.base == b++ && - UNSAFE.compareAndSwapObject(q, u, t, null)) { - if (joinMe.status < 0) { - UNSAFE.putObjectVolatile(q, u, t); - return; // back out on cancel - } - v.base = b; - v.stealHint = pid; - UNSAFE.putOrderedObject(this, currentStealOffset, t); - t.quietlyExec(); - UNSAFE.putOrderedObject(this, currentStealOffset, ps); - } - } - // Try to descend to find v's stealer - ForkJoinTask<?> next = v.currentJoin; - if (task.status < 0 || next == null || next == task || - joinMe.status < 0) - return; - task = next; - thread = v; } + return running; } /**
--- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Wed Feb 09 09:32:04 2011 -0500 @@ -1029,6 +1029,8 @@ * 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 deque in reverse order */ public Iterator<E> descendingIterator() { return new DescendingItr();
--- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Feb 09 09:32:04 2011 -0500 @@ -189,14 +189,14 @@ } /** - * Creates a node and links it at end of queue. + * Links node at end of queue. * - * @param x the item + * @param node the node */ - private void enqueue(E x) { + private void enqueue(Node<E> node) { // assert putLock.isHeldByCurrentThread(); // assert last.next == null; - last = last.next = new Node<E>(x); + last = last.next = node; } /** @@ -282,7 +282,7 @@ throw new NullPointerException(); if (n == capacity) throw new IllegalStateException("Queue full"); - enqueue(e); + enqueue(new Node<E>(e)); ++n; } count.set(n); @@ -332,6 +332,7 @@ // 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); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); @@ -347,7 +348,7 @@ while (count.get() == capacity) { notFull.await(); } - enqueue(e); + enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -382,7 +383,7 @@ return false; nanos = notFull.awaitNanos(nanos); } - enqueue(e); + enqueue(new Node<E>(e)); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -411,11 +412,12 @@ if (count.get() == capacity) return false; int c = -1; + Node<E> node = new Node(e); final ReentrantLock putLock = this.putLock; putLock.lock(); try { if (count.get() < capacity) { - enqueue(e); + enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -560,6 +562,27 @@ } /** + * 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; + fullyLock(); + try { + for (Node<E> p = head.next; p != null; p = p.next) + if (o.equals(p.item)) + return true; + return false; + } finally { + fullyUnlock(); + } + } + + /** * Returns an array containing all of the elements in this queue, in * proper sequence. * @@ -645,7 +668,20 @@ public String toString() { fullyLock(); try { - return super.toString(); + Node<E> p = head.next; + if (p == null) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + E e = p.item; + sb.append(e == this ? "(this Collection)" : e); + p = p.next; + if (p == null) + return sb.append(']').toString(); + sb.append(',').append(' '); + } } finally { fullyUnlock(); } @@ -727,12 +763,14 @@ /** * Returns an iterator over the elements in this queue in proper sequence. - * The returned {@code Iterator} is a "weakly consistent" iterator that + * 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. + * 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 */
--- a/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Feb 09 09:32:04 2011 -0500 @@ -37,10 +37,10 @@ import java.util.AbstractQueue; import java.util.Collection; -import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** @@ -450,7 +450,7 @@ } final boolean casItem(Object cmp, Object val) { - // assert cmp == null || cmp.getClass() != Node.class; + // assert cmp == null || cmp.getClass() != Node.class; return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } @@ -516,7 +516,7 @@ * Tries to artificially match a data node -- used by remove. */ final boolean tryMatchData() { - // assert isData; + // assert isData; Object x = item; if (x != null && x != this && casItem(x, null)) { LockSupport.unpark(waiter); @@ -569,7 +569,7 @@ @SuppressWarnings("unchecked") static <E> E cast(Object item) { - // assert item == null || item.getClass() != Node.class; + // assert item == null || item.getClass() != Node.class; return (E) item; } @@ -588,7 +588,8 @@ throw new NullPointerException(); Node s = null; // the node to append, if needed - retry: for (;;) { // restart on append race + retry: + for (;;) { // restart on append race for (Node h = head, p = h; p != null;) { // find & match first node boolean isData = p.isData; @@ -599,7 +600,7 @@ 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)) { + if (head == h && casHead(h, n == null ? q : n)) { h.forgetNext(); break; } // advance and retry @@ -684,7 +685,7 @@ for (;;) { Object item = s.item; if (item != e) { // matched - // assert item != s; + // assert item != s; s.forgetContents(); // avoid garbage return this.<E>cast(item); } @@ -809,22 +810,61 @@ * Moves to next node after prev, or first node if prev null. */ private void advance(Node prev) { - lastPred = lastRet; - lastRet = prev; - for (Node p = (prev == null) ? head : succ(prev); - p != null; p = succ(p)) { - Object item = p.item; - if (p.isData) { - if (item != null && item != p) { - nextItem = LinkedTransferQueue.this.<E>cast(item); - nextNode = p; + /* + * 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() { @@ -844,10 +884,12 @@ } public final void remove() { - Node p = lastRet; - if (p == null) throw new IllegalStateException(); - if (p.tryMatchData()) - unsplice(lastPred, p); + final Node lastRet = this.lastRet; + if (lastRet == null) + throw new IllegalStateException(); + this.lastRet = null; + if (lastRet.tryMatchData()) + unsplice(lastPred, lastRet); } } @@ -997,8 +1039,7 @@ * 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 BlockingQueue#offer(Object) BlockingQueue.offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { @@ -1130,15 +1171,15 @@ } /** - * Returns an iterator over the elements in this queue in proper - * sequence, from head to tail. + * 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 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. + * 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 */ @@ -1203,6 +1244,28 @@ } /** + * 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. *
--- a/jdk/src/share/classes/java/util/concurrent/Phaser.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/Phaser.java Wed Feb 09 09:32:04 2011 -0500 @@ -35,6 +35,8 @@ 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; @@ -61,38 +63,38 @@ * Phaser} may be repeatedly awaited. Method {@link * #arriveAndAwaitAdvance} has effect analogous to {@link * java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each - * generation of a {@code Phaser} has an associated phase number. The - * phase number starts at zero, and advances when all parties arrive - * at the barrier, wrapping around to zero after reaching {@code + * 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 barrier and upon awaiting + * 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 at a - * barrier. These methods do not block, but return an associated - * <em>arrival phase number</em>; that is, the phase number of - * the barrier to which the arrival applied. When the final - * party for a given phase arrives, an optional barrier action - * is performed and the phase advances. Barrier actions, - * performed by the party triggering a phase advance, 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}. + * {@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 barrier advances to (or is already at) a different phase. + * 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 barrier. If necessary, you can perform any + * 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}, @@ -101,26 +103,39 @@ * * </ul> * - * <p> <b>Termination.</b> A {@code Phaser} may enter a - * <em>termination</em> state in which all synchronization methods - * immediately return without updating phaser state or waiting for - * advance, and indicating (via a negative phase value) that execution - * is complete. Termination is triggered when an invocation of {@code - * onAdvance} returns {@code true}. 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>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., arranged - * in tree structures) to reduce contention. Phasers with large - * numbers of parties that would otherwise experience heavy + * <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 @@ -136,9 +151,9 @@ * <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: + * 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) { @@ -208,34 +223,32 @@ * }}</pre> * * - * <p>To create a set of tasks using a tree of phasers, - * you could use code of the following form, assuming a - * Task class with a constructor accepting a phaser that - * it registers for upon construction: + * <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[] actions, int lo, int hi, Phaser ph) { + * 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(actions, i, j, new Phaser(ph)); + * build(tasks, i, j, new Phaser(ph)); * } * } else { * for (int i = lo; i < hi; ++i) - * actions[i] = new Task(ph); + * tasks[i] = new Task(ph); * // assumes new Task(ph) performs ph.register() * } - * } - * // .. initially called, for n tasks via - * build(new Task[n], 0, n, new Phaser());}</pre> + * }}</pre> * * The best value of {@code TASKS_PER_PHASER} depends mainly on - * expected barrier synchronization rates. A value as low as four may - * be appropriate for extremely small per-barrier task bodies (thus + * 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. * - * </pre> - * * <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 @@ -253,60 +266,66 @@ */ /** - * Barrier state representation. Conceptually, a barrier contains - * four values: + * Primary state representation, holding four bit-fields: * - * * parties -- the number of parties to wait (16 bits) - * * unarrived -- the number of parties yet to hit barrier (16 bits) - * * phase -- the generation of the barrier (31 bits) - * * terminated -- set if barrier is terminated (1 bit) + * 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) * - * However, to efficiently maintain atomicity, these values are - * packed into a single (atomic) long. Termination uses the sign - * bit of 32 bit representation of phase, so phase is set to -1 on - * termination. Good performance relies on keeping state decoding - * and encoding simple, and keeping race windows short. + * 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). * - * Note: there are some cheats in arrive() that rely on unarrived - * count being lowest 16 bits. + * 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 ushortMask = 0xffff; - private static final int phaseMask = 0x7fffffff; + 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 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 EMPTY = 1; + + // The following unpacking methods are usually manually inlined private static int unarrivedOf(long s) { - return (int) (s & ushortMask); + int counts = (int)s; + return (counts == EMPTY) ? 0 : counts & UNARRIVED_MASK; } private static int partiesOf(long s) { - return ((int) s) >>> 16; + return (int)s >>> PARTIES_SHIFT; } private static int phaseOf(long s) { - return (int) (s >>> 32); + return (int)(s >>> PHASE_SHIFT); } private static int arrivedOf(long s) { - return partiesOf(s) - unarrivedOf(s); - } - - private static long stateFor(int phase, int parties, int unarrived) { - return ((((long) phase) << 32) | (((long) parties) << 16) | - (long) unarrived); - } - - private static long trippedStateFor(int phase, int parties) { - long lp = (long) parties; - return (((long) phase) << 32) | (lp << 16) | lp; - } - - /** - * Returns message string for bad bounds exceptions. - */ - private static String badBounds(int parties, int unarrived) { - return ("Attempt to set " + unarrived + - " unarrived of " + parties + " parties"); + int counts = (int)s; + return (counts == EMPTY) ? 0 : + (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK); } /** @@ -315,70 +334,180 @@ private final Phaser parent; /** - * The root of phaser tree. Equals this if not in a tree. Used to - * support faster state push-down. + * The root of phaser tree. Equals this if not in a tree. */ private final Phaser root; - // Wait queues - /** * Heads of Treiber stacks for waiting threads. To eliminate - * contention while releasing some threads while adding others, we + * 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 = new AtomicReference<QNode>(); - private final AtomicReference<QNode> oddQ = new AtomicReference<QNode>(); + private final AtomicReference<QNode> evenQ; + private final AtomicReference<QNode> oddQ; private AtomicReference<QNode> queueFor(int phase) { return ((phase & 1) == 0) ? evenQ : oddQ; } /** - * Returns current state, first resolving lagged propagation from - * root if necessary. + * Returns message string for bounds exceptions on arrival. */ - private long getReconciledState() { - return (parent == null) ? state : reconcileState(); + private String badArrive(long s) { + return "Attempted arrival of unregistered party for " + + stateToString(s); } /** - * Recursively resolves state. + * Returns message string for bounds exceptions on registration. */ - private long reconcileState() { - Phaser p = parent; - long s = state; - if (p != null) { - while (unarrivedOf(s) == 0 && phaseOf(s) != phaseOf(root.state)) { - long parentState = p.getReconciledState(); - int parentPhase = phaseOf(parentState); - int phase = phaseOf(s = state); - if (phase != parentPhase) { - long next = trippedStateFor(parentPhase, partiesOf(s)); - if (casState(s, next)) { - releaseWaiters(phase); - s = next; + 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 deregister false for arrive, true for arriveAndDeregister + */ + private int doArrive(boolean deregister) { + int adj = deregister ? ONE_ARRIVAL|ONE_PARTY : ONE_ARRIVAL; + final Phaser root = this.root; + for (;;) { + long s = (root == this) ? state : reconcileState(); + int phase = (int)(s >>> PHASE_SHIFT); + int counts = (int)s; + int unarrived = (counts & UNARRIVED_MASK) - 1; + if (phase < 0) + return phase; + else if (counts == EMPTY || unarrived < 0) { + if (root == this || reconcileState() == s) + throw new IllegalStateException(badArrive(s)); + } + else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adj)) { + if (unarrived == 0) { + long n = s & PARTIES_MASK; // base of next state + int nextUnarrived = (int)n >>> PARTIES_SHIFT; + if (root != this) + return parent.doArrive(nextUnarrived == 0); + if (onAdvance(phase, nextUnarrived)) + n |= TERMINATION_BIT; + else if (nextUnarrived == 0) + n |= EMPTY; + else + n |= nextUnarrived; + n |= (long)((phase + 1) & MAX_PHASE) << PHASE_SHIFT; + UNSAFE.compareAndSwapLong(this, stateOffset, s, n); + releaseWaiters(phase); + } + 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 adj = ((long)registrations << PARTIES_SHIFT) | registrations; + final Phaser parent = this.parent; + int phase; + for (;;) { + long s = state; + int counts = (int)s; + int parties = counts >>> PARTIES_SHIFT; + int unarrived = counts & UNARRIVED_MASK; + if (registrations > MAX_PARTIES - parties) + throw new IllegalStateException(badRegister(s)); + else if ((phase = (int)(s >>> PHASE_SHIFT)) < 0) + break; + else 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 + adj)) + break; + } + } + else if (parent == null) { // 1st root registration + long next = ((long)phase << PHASE_SHIFT) | adj; + if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next)) + break; + } + else { + synchronized (this) { // 1st sub registration + if (state == s) { // recheck under lock + parent.doRegister(1); + do { // force current phase + phase = (int)(root.state >>> PHASE_SHIFT); + // assert phase < 0 || (int)state == EMPTY; + } while (!UNSAFE.compareAndSwapLong + (this, stateOffset, state, + ((long)phase << PHASE_SHIFT) | adj)); + 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). + * However, this method may also be called when "floating" + * subphasers with possibly some unarrived parties are merely + * catching up to current phase, in which case counts are + * unaffected. + * + * @return reconciled state + */ + private long reconcileState() { + final Phaser root = this.root; + long s = state; + if (root != this) { + int phase, u, p; + // CAS root phase with current parties; possibly trip unarrived + while ((phase = (int)(root.state >>> PHASE_SHIFT)) != + (int)(s >>> PHASE_SHIFT) && + !UNSAFE.compareAndSwapLong + (this, stateOffset, s, + s = (((long)phase << PHASE_SHIFT) | + (s & PARTIES_MASK) | + ((p = (int)s >>> PARTIES_SHIFT) == 0 ? EMPTY : + (u = (int)s & UNARRIVED_MASK) == 0 ? p : u)))) + s = state; + } return s; } /** - * Creates a new phaser without any initially registered parties, - * initial phase number 0, and no parent. Any thread using this + * 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); + this(null, 0); } /** - * Creates a new phaser with the given numbers of registered - * unarrived parties, initial phase number 0, and no parent. + * 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 trip barrier + * @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 */ @@ -387,54 +516,62 @@ } /** - * Creates a new phaser with the given parent, without any - * initially registered parties. If parent is non-null this phaser - * is registered with the parent and its initial phase number is - * the same as that of parent phaser. + * Equivalent to {@link #Phaser(Phaser, int) Phaser(parent, 0)}. * * @param parent the parent phaser */ public Phaser(Phaser parent) { - int phase = 0; - this.parent = parent; - if (parent != null) { - this.root = parent.root; - phase = parent.register(); - } - else - this.root = this; - this.state = trippedStateFor(phase, 0); + this(parent, 0); } /** - * Creates a new phaser with the given parent and numbers of - * registered unarrived parties. If parent is non-null, this phaser - * is registered with the parent and its initial phase number is - * the same as that of parent phaser. + * 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 trip barrier + * @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 < 0 || parties > ushortMask) + if (parties >>> PARTIES_SHIFT != 0) throw new IllegalArgumentException("Illegal number of parties"); int phase = 0; this.parent = parent; if (parent != null) { - this.root = parent.root; - phase = parent.register(); + final Phaser root = parent.root; + this.root = root; + this.evenQ = root.evenQ; + this.oddQ = root.oddQ; + if (parties != 0) + phase = parent.doRegister(1); } - else + else { this.root = this; - this.state = trippedStateFor(phase, parties); + 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. + * 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 + * @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 */ @@ -444,11 +581,22 @@ /** * 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 parties required to trip barrier - * @return the arrival phase number to which this registration applied + * @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) @@ -459,258 +607,210 @@ } /** - * Shared code for register, bulkRegister - */ - private int doRegister(int registrations) { - int phase; - for (;;) { - long s = getReconciledState(); - phase = phaseOf(s); - int unarrived = unarrivedOf(s) + registrations; - int parties = partiesOf(s) + registrations; - if (phase < 0) - break; - if (parties > ushortMask || unarrived > ushortMask) - throw new IllegalStateException(badBounds(parties, unarrived)); - if (phase == phaseOf(root.state) && - casState(s, stateFor(phase, parties, unarrived))) - break; - } - return phase; - } - - /** - * Arrives at the barrier, but does not wait for others. (You can - * in turn wait for others via {@link #awaitAdvance}). It is an - * unenforced usage error for an unregistered party to invoke this - * method. + * 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() { - int phase; - for (;;) { - long s = state; - phase = phaseOf(s); - if (phase < 0) - break; - int parties = partiesOf(s); - int unarrived = unarrivedOf(s) - 1; - if (unarrived > 0) { // Not the last arrival - if (casState(s, s - 1)) // s-1 adds one arrival - break; - } - else if (unarrived == 0) { // the last arrival - Phaser par = parent; - if (par == null) { // directly trip - if (casState - (s, - trippedStateFor(onAdvance(phase, parties) ? -1 : - ((phase + 1) & phaseMask), parties))) { - releaseWaiters(phase); - break; - } - } - else { // cascade to parent - if (casState(s, s - 1)) { // zeroes unarrived - par.arrive(); - reconcileState(); - break; - } - } - } - else if (phase != phaseOf(root.state)) // or if unreconciled - reconcileState(); - else - throw new IllegalStateException(badBounds(parties, unarrived)); - } - return phase; + return doArrive(false); } /** - * Arrives at the barrier and deregisters from it without waiting - * for others. Deregistration reduces the number of parties - * required to trip the barrier in future phases. If this phaser + * 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 also arrives at and is deregistered - * from its parent. It is an unenforced usage error for an - * unregistered party to invoke this method. + * 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() { - // similar code to arrive, but too different to merge - Phaser par = parent; - int phase; - for (;;) { - long s = state; - phase = phaseOf(s); - if (phase < 0) - break; - int parties = partiesOf(s) - 1; - int unarrived = unarrivedOf(s) - 1; - if (parties >= 0) { - if (unarrived > 0 || (unarrived == 0 && par != null)) { - if (casState - (s, - stateFor(phase, parties, unarrived))) { - if (unarrived == 0) { - par.arriveAndDeregister(); - reconcileState(); - } - break; - } - continue; - } - if (unarrived == 0) { - if (casState - (s, - trippedStateFor(onAdvance(phase, parties) ? -1 : - ((phase + 1) & phaseMask), parties))) { - releaseWaiters(phase); - break; - } - continue; - } - if (par != null && phase != phaseOf(root.state)) { - reconcileState(); - continue; - } - } - throw new IllegalStateException(badBounds(parties, unarrived)); - } - return phase; + return doArrive(true); } /** - * Arrives at the barrier and awaits others. Equivalent in effect + * 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 awaitAdvance - * method. If instead you need to deregister upon arrival use - * {@code arriveAndDeregister}. It is an unenforced usage error - * for an unregistered party to invoke this method. + * construction using one of the other forms of the {@code + * awaitAdvance} method. If instead you need to deregister upon + * arrival, use {@code awaitAdvance(arriveAndDeregister())}. * - * @return the arrival phase number, or a negative number if terminated + * <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() { - return awaitAdvance(arrive()); + // 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); + int counts = (int)s; + int unarrived = (counts & UNARRIVED_MASK) - 1; + if (phase < 0) + return phase; + else if (counts == EMPTY || unarrived < 0) { + if (reconcileState() == s) + throw new IllegalStateException(badArrive(s)); + } + else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, + s -= ONE_ARRIVAL)) { + if (unarrived != 0) + 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 the barrier to advance from the given phase - * value, returning immediately if the current phase of the - * barrier is not equal to the given phase value or this barrier - * is terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * 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 its variants - * @return the next arrival phase number, or a negative value - * if terminated or argument is negative + * 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; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvance(phase); - // Fall here even if parent waited, to reconcile and help release - return untimedWait(phase); + if (p == phase) + return root.internalAwaitAdvance(phase, null); + return p; } /** - * Awaits the phase of the barrier to advance from the given phase + * 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 of - * the barrier is not equal to the given phase value or this - * barrier is terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * 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 its variants - * @return the next arrival phase number, or a negative value - * if terminated or argument is negative + * 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; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvanceInterruptibly(phase); - return interruptibleWait(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 the barrier to advance from the given phase + * 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 of the barrier is - * not equal to the given phase value or this barrier is - * terminated. It is an unenforced usage error for an - * unregistered party to invoke this method. + * 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 its variants + * 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 a negative value - * if terminated or argument is negative + * @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; - long s = getReconciledState(); - int p = phaseOf(s); - if (p != phase) - return p; - if (unarrivedOf(s) == 0 && parent != null) - parent.awaitAdvanceInterruptibly(phase, timeout, unit); - return timedWait(phase, unit.toNanos(timeout)); + 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 barrier to enter termination state. Counts of - * arrived and registered parties are unaffected. If this phaser - * has a parent, it too is terminated. This method may be useful - * for coordinating recovery after one or more tasks encounter + * 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() { - for (;;) { - long s = getReconciledState(); - int phase = phaseOf(s); - int parties = partiesOf(s); - int unarrived = unarrivedOf(s); - if (phase < 0 || - casState(s, stateFor(-1, parties, unarrived))) { + // 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); releaseWaiters(1); - if (parent != null) - parent.forceTermination(); return; } } @@ -719,16 +819,18 @@ /** * 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. + * 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 phaseOf(getReconciledState()); + return (int)(root.state >>> PHASE_SHIFT); } /** - * Returns the number of parties registered at this barrier. + * Returns the number of parties registered at this phaser. * * @return the number of parties */ @@ -738,22 +840,24 @@ /** * Returns the number of registered parties that have arrived at - * the current phase of this barrier. + * 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(state); + return arrivedOf(reconcileState()); } /** * Returns the number of registered parties that have not yet - * arrived at the current phase of this barrier. + * 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(state); + return unarrivedOf(reconcileState()); } /** @@ -776,52 +880,56 @@ } /** - * Returns {@code true} if this barrier has been terminated. + * Returns {@code true} if this phaser has been terminated. * - * @return {@code true} if this barrier has been terminated + * @return {@code true} if this phaser has been terminated */ public boolean isTerminated() { - return getPhase() < 0; + 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 tripping the barrier (when all other + * upon arrival of the party advancing this phaser (when all other * waiting parties are dormant). If this method returns {@code - * true}, then, rather than advance the phase number, this barrier - * will be set to a final termination state, 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 trip the barrier, in - * which case no advance occurs. + * 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. (When called from within - * an implementation of {@code onAdvance} the values returned by - * methods such as {@code getPhase} may or may not reliably - * indicate the state to which this transition applies.) + * 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>The default version returns {@code true} when the number of - * registered parties is zero. Normally, overrides that arrange - * termination for other reasons should also preserve this - * property. + * <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>You may override this method to perform an action with side - * effects visible to participating tasks, but it is only sensible - * to do so in designs where all parties register before any - * arrive, and all {@link #awaitAdvance} at each phase. - * Otherwise, you cannot ensure lack of interference from other - * parties during the invocation of this method. Additionally, - * method {@code onAdvance} may be invoked more than once per - * transition if registrations are intermixed with arrivals. + * <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}: * - * @param phase the phase number on entering the barrier + * <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 barrier should terminate + * @return {@code true} if this phaser should terminate */ protected boolean onAdvance(int phase, int registeredParties) { - return registeredParties <= 0; + return registeredParties == 0; } /** @@ -831,17 +939,138 @@ * followed by the number of registered parties, and {@code * "arrived = "} followed by the number of arrived parties. * - * @return a string identifying this barrier, as well as its state + * @return a string identifying this phaser, as well as its state */ public String toString() { - long s = getReconciledState(); + 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) + "]"; } - // methods for waiting + // 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 from root node. + * + * @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) { + 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 @@ -849,174 +1078,61 @@ static final class QNode implements ForkJoinPool.ManagedBlocker { final Phaser phaser; final int phase; - final long startTime; - final long nanos; + final boolean interruptible; final boolean timed; - final boolean interruptible; - volatile boolean wasInterrupted = false; + 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 startTime, long nanos) { + boolean timed, long nanos) { this.phaser = phaser; this.phase = phase; + this.interruptible = interruptible; + this.nanos = nanos; this.timed = timed; - this.interruptible = interruptible; - this.startTime = startTime; - this.nanos = nanos; + this.lastTime = timed ? System.nanoTime() : 0L; thread = Thread.currentThread(); } + public boolean isReleasable() { - return (thread == null || - phaser.getPhase() != phase || - (interruptible && wasInterrupted) || - (timed && (nanos - (System.nanoTime() - startTime)) <= 0)); + 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 (Thread.interrupted()) { - wasInterrupted = true; - if (interruptible) - return true; - } - if (!timed) + if (isReleasable()) + return true; + else if (!timed) LockSupport.park(this); - else { - long waitTime = nanos - (System.nanoTime() - startTime); - if (waitTime <= 0) - return true; - LockSupport.parkNanos(this, waitTime); - } + else if (nanos > 0) + LockSupport.parkNanos(this, nanos); return isReleasable(); } - void signal() { - Thread t = thread; - if (t != null) { - thread = null; - LockSupport.unpark(t); - } - } - boolean doWait() { - if (thread != null) { - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { - } - } - return wasInterrupted; - } - - } - - /** - * Removes and signals waiting threads from wait queue. - */ - private void releaseWaiters(int phase) { - AtomicReference<QNode> head = queueFor(phase); - QNode q; - while ((q = head.get()) != null) { - if (head.compareAndSet(q, q.next)) - q.signal(); - } - } - - /** - * Tries to enqueue given node in the appropriate wait queue. - * - * @return true if successful - */ - private boolean tryEnqueue(QNode node) { - AtomicReference<QNode> head = queueFor(node.phase); - return head.compareAndSet(node.next = head.get(), node); - } - - /** - * Enqueues node and waits unless aborted or signalled. - * - * @return current phase - */ - private int untimedWait(int phase) { - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase) { - if (Thread.interrupted()) - interrupted = true; - else if (node == null) - node = new QNode(this, phase, false, false, 0, 0); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - releaseWaiters(phase); - if (interrupted) - Thread.currentThread().interrupt(); - return p; - } - - /** - * Interruptible version - * @return current phase - */ - private int interruptibleWait(int phase) throws InterruptedException { - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase && !interrupted) { - if (Thread.interrupted()) - interrupted = true; - else if (node == null) - node = new QNode(this, phase, true, false, 0, 0); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - if (p != phase || (p = getPhase()) != phase) - releaseWaiters(phase); - if (interrupted) - throw new InterruptedException(); - return p; - } - - /** - * Timeout version. - * @return current phase - */ - private int timedWait(int phase, long nanos) - throws InterruptedException, TimeoutException { - long startTime = System.nanoTime(); - QNode node = null; - boolean queued = false; - boolean interrupted = false; - int p; - while ((p = getPhase()) == phase && !interrupted) { - if (Thread.interrupted()) - interrupted = true; - else if (nanos - (System.nanoTime() - startTime) <= 0) - break; - else if (node == null) - node = new QNode(this, phase, true, true, startTime, nanos); - else if (!queued) - queued = tryEnqueue(node); - else - interrupted = node.doWait(); - } - if (node != null) - node.thread = null; - if (p != phase || (p = getPhase()) != phase) - releaseWaiters(phase); - if (interrupted) - throw new InterruptedException(); - if (p == phase) - throw new TimeoutException(); - return p; } // Unsafe mechanics @@ -1025,10 +1141,6 @@ private static final long stateOffset = objectFieldOffset("state", Phaser.class); - private final boolean casState(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, stateOffset, cmp, val); - } - private static long objectFieldOffset(String field, Class<?> klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
--- a/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Wed Feb 09 09:32:04 2011 -0500 @@ -43,11 +43,11 @@ * the same ordering rules as class {@link PriorityQueue} and supplies * blocking retrieval operations. While this queue is logically * unbounded, attempted additions may fail due to resource exhaustion - * (causing <tt>OutOfMemoryError</tt>). This class does not permit - * <tt>null</tt> elements. A priority queue relying on {@linkplain + * (causing {@code OutOfMemoryError}). This class does not permit + * {@code null} elements. A priority queue relying on {@linkplain * Comparable natural ordering} also does not permit insertion of * non-comparable objects (doing so results in - * <tt>ClassCastException</tt>). + * {@code ClassCastException}). * * <p>This class and its iterator implement all of the * <em>optional</em> methods of the {@link Collection} and {@link @@ -55,7 +55,7 @@ * #iterator()} is <em>not</em> guaranteed to traverse the elements of * the PriorityBlockingQueue in any particular order. If you need * ordered traversal, consider using - * <tt>Arrays.sort(pq.toArray())</tt>. Also, method <tt>drainTo</tt> + * {@code Arrays.sort(pq.toArray())}. Also, method {@code drainTo} * can be used to <em>remove</em> some or all elements in priority * order and place them in another collection. * @@ -65,12 +65,12 @@ * secondary key to break ties in primary priority values. For * example, here is a class that applies first-in-first-out * tie-breaking to comparable elements. To use it, you would insert a - * <tt>new FIFOEntry(anEntry)</tt> instead of a plain entry object. + * {@code new FIFOEntry(anEntry)} instead of a plain entry object. * - * <pre> - * class FIFOEntry<E extends Comparable<? super E>> - * implements Comparable<FIFOEntry<E>> { - * final static AtomicLong seq = new AtomicLong(); + * <pre> {@code + * class FIFOEntry<E extends Comparable<? super E>> + * implements Comparable<FIFOEntry<E>> { + * static final AtomicLong seq = new AtomicLong(0); * final long seqNum; * final E entry; * public FIFOEntry(E entry) { @@ -78,13 +78,13 @@ * this.entry = entry; * } * public E getEntry() { return entry; } - * public int compareTo(FIFOEntry<E> other) { + * public int compareTo(FIFOEntry<E> other) { * int res = entry.compareTo(other.entry); - * if (res == 0 && other.entry != this.entry) - * res = (seqNum < other.seqNum ? -1 : 1); + * if (res == 0 && other.entry != this.entry) + * res = (seqNum < other.seqNum ? -1 : 1); * return res; * } - * }</pre> + * }}</pre> * * <p>This class is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> @@ -98,34 +98,102 @@ implements BlockingQueue<E>, java.io.Serializable { private static final long serialVersionUID = 5595510919245408276L; - private final PriorityQueue<E> q; - private final ReentrantLock lock = new ReentrantLock(true); - private final Condition notEmpty = lock.newCondition(); + /* + * The implementation uses an array-based binary heap, with public + * operations protected with a single lock. However, allocation + * during resizing uses a simple spinlock (used only while not + * holding main lock) in order to allow takes to operate + * concurrently with allocation. This avoids repeated + * postponement of waiting consumers and consequent element + * build-up. The need to back away from lock during allocation + * makes it impossible to simply wrap delegated + * 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 + * transiently doubling overhead. + */ /** - * Creates a <tt>PriorityBlockingQueue</tt> with the default + * Default array capacity. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Priority queue represented as a balanced binary heap: the two + * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The + * priority queue is ordered by comparator, or by the elements' + * natural ordering, if comparator is null: For each node n in the + * heap and each descendant d of n, n <= d. The element with the + * lowest value is in queue[0], assuming the queue is nonempty. + */ + private transient Object[] queue; + + /** + * The number of elements in the priority queue. + */ + private transient int size; + + /** + * The comparator, or null if priority queue uses elements' + * natural ordering. + */ + private transient Comparator<? super E> comparator; + + /** + * Lock used for all public operations + */ + private final ReentrantLock lock; + + /** + * Condition for blocking when empty + */ + private final Condition notEmpty; + + /** + * Spinlock for allocation, acquired via CAS. + */ + private transient volatile int allocationSpinLock; + + /** + * A plain PriorityQueue used only for serialization, + * to maintain compatibility with previous versions + * of this class. Non-null only during serialization/deserialization. + */ + private PriorityQueue q; + + /** + * Creates a {@code PriorityBlockingQueue} with the default * initial capacity (11) that orders its elements according to * their {@linkplain Comparable natural ordering}. */ public PriorityBlockingQueue() { - q = new PriorityQueue<E>(); + this(DEFAULT_INITIAL_CAPACITY, null); } /** - * Creates a <tt>PriorityBlockingQueue</tt> with the specified + * Creates a {@code PriorityBlockingQueue} with the specified * initial capacity that orders its elements according to their * {@linkplain Comparable natural ordering}. * * @param initialCapacity the initial capacity for this priority queue - * @throws IllegalArgumentException if <tt>initialCapacity</tt> is less + * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ public PriorityBlockingQueue(int initialCapacity) { - q = new PriorityQueue<E>(initialCapacity, null); + this(initialCapacity, null); } /** - * Creates a <tt>PriorityBlockingQueue</tt> with the specified initial + * Creates a {@code PriorityBlockingQueue} with the specified initial * capacity that orders its elements according to the specified * comparator. * @@ -133,16 +201,21 @@ * @param comparator the comparator that will be used to order this * priority queue. If {@code null}, the {@linkplain Comparable * natural ordering} of the elements will be used. - * @throws IllegalArgumentException if <tt>initialCapacity</tt> is less + * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ public PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) { - q = new PriorityQueue<E>(initialCapacity, comparator); + if (initialCapacity < 1) + throw new IllegalArgumentException(); + this.lock = new ReentrantLock(); + this.notEmpty = lock.newCondition(); + this.comparator = comparator; + this.queue = new Object[initialCapacity]; } /** - * Creates a <tt>PriorityBlockingQueue</tt> containing the elements + * Creates a {@code PriorityBlockingQueue} containing the elements * in the specified collection. If the specified collection is a * {@link SortedSet} or a {@link PriorityQueue}, this * priority queue will be ordered according to the same ordering. @@ -158,14 +231,215 @@ * of its elements are null */ public PriorityBlockingQueue(Collection<? extends E> c) { - q = new PriorityQueue<E>(c); + this.lock = new ReentrantLock(); + this.notEmpty = lock.newCondition(); + boolean heapify = true; // true if not known to be in heap order + boolean screen = true; // true if must screen for nulls + if (c instanceof SortedSet<?>) { + SortedSet<? extends E> ss = (SortedSet<? extends E>) c; + this.comparator = (Comparator<? super E>) ss.comparator(); + heapify = false; + } + else if (c instanceof PriorityBlockingQueue<?>) { + PriorityBlockingQueue<? extends E> pq = + (PriorityBlockingQueue<? extends E>) c; + this.comparator = (Comparator<? super E>) pq.comparator(); + screen = false; + if (pq.getClass() == PriorityBlockingQueue.class) // exact match + heapify = false; + } + Object[] a = c.toArray(); + int n = a.length; + // If c.toArray incorrectly doesn't return Object[], copy it. + if (a.getClass() != Object[].class) + a = Arrays.copyOf(a, n, Object[].class); + if (screen && (n == 1 || this.comparator != null)) { + for (int i = 0; i < n; ++i) + if (a[i] == null) + throw new NullPointerException(); + } + this.queue = a; + this.size = n; + if (heapify) + heapify(); + } + + /** + * Tries to grow array to accommodate at least one more element + * (but normally expand by about 50%), giving up (allowing retry) + * on contention (which we expect to be rare). Call only while + * holding lock. + * + * @param array the heap array + * @param oldCap the length of the array + */ + private void tryGrow(Object[] array, int oldCap) { + lock.unlock(); // must release and then re-acquire main lock + Object[] newArray = null; + if (allocationSpinLock == 0 && + UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset, + 0, 1)) { + try { + int newCap = oldCap + ((oldCap < 64) ? + (oldCap + 2) : // grow faster if small + (oldCap >> 1)); + if (newCap - MAX_ARRAY_SIZE > 0) { // possible overflow + int minCap = oldCap + 1; + if (minCap < 0 || minCap > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(); + newCap = MAX_ARRAY_SIZE; + } + if (newCap > oldCap && queue == array) + newArray = new Object[newCap]; + } finally { + allocationSpinLock = 0; + } + } + if (newArray == null) // back off if another thread is allocating + Thread.yield(); + lock.lock(); + if (newArray != null && queue == array) { + queue = newArray; + System.arraycopy(array, 0, newArray, 0, oldCap); + } + } + + /** + * Mechanics for poll(). Call only while holding lock. + */ + private E extract() { + E result; + int n = size - 1; + if (n < 0) + result = null; + else { + Object[] array = queue; + result = (E) array[0]; + E x = (E) array[n]; + array[n] = null; + Comparator<? super E> cmp = comparator; + if (cmp == null) + siftDownComparable(0, x, array, n); + else + siftDownUsingComparator(0, x, array, n, cmp); + size = n; + } + return result; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * To simplify and speed up coercions and comparisons. the + * Comparable and Comparator versions are separated into different + * methods that are otherwise identical. (Similarly for siftDown.) + * These methods are static, with heap state as arguments, to + * simplify use in light of possible comparator exceptions. + * + * @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; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = array[parent]; + if (key.compareTo((T) e) >= 0) + break; + array[k] = e; + k = parent; + } + array[k] = key; + } + + private static <T> void siftUpUsingComparator(int k, T x, Object[] array, + Comparator<? super T> cmp) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = array[parent]; + if (cmp.compare(x, (T) e) >= 0) + break; + array[k] = e; + k = parent; + } + array[k] = x; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * demoting x down the tree repeatedly until it is less than or + * equal to its children or is a leaf. + * + * @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 siftDownComparable(int k, T x, Object[] array, + int n) { + Comparable<? super T> key = (Comparable<? super T>)x; + int half = n >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = array[child]; + int right = child + 1; + if (right < n && + ((Comparable<? super T>) c).compareTo((T) array[right]) > 0) + c = array[child = right]; + if (key.compareTo((T) c) <= 0) + break; + array[k] = c; + k = child; + } + array[k] = key; + } + + private static <T> void siftDownUsingComparator(int k, T x, Object[] array, + int n, + Comparator<? super T> cmp) { + int half = n >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = array[child]; + int right = child + 1; + if (right < n && cmp.compare((T) c, (T) array[right]) > 0) + c = array[child = right]; + if (cmp.compare(x, (T) c) <= 0) + break; + array[k] = c; + k = child; + } + array[k] = x; + } + + /** + * Establishes the heap invariant (described above) in the entire tree, + * assuming nothing about the order of the elements prior to the call. + */ + private void heapify() { + Object[] array = queue; + int n = size; + int half = (n >>> 1) - 1; + Comparator<? super E> cmp = comparator; + if (cmp == null) { + for (int i = half; i >= 0; i--) + siftDownComparable(i, (E) array[i], array, n); + } + else { + for (int i = half; i >= 0; i--) + siftDownUsingComparator(i, (E) array[i], array, n, cmp); + } } /** * Inserts the specified element into this priority queue. * * @param e the element to add - * @return <tt>true</tt> (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering @@ -177,30 +451,41 @@ /** * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never return {@code false}. * * @param e the element to add - * @return <tt>true</tt> (as specified by {@link Queue#offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); + int n, cap; + Object[] array; + while ((n = size) >= (cap = (array = queue).length)) + tryGrow(array, cap); try { - boolean ok = q.offer(e); - assert ok; + Comparator<? super E> cmp = comparator; + if (cmp == null) + siftUpComparable(n, e, array); + else + siftUpUsingComparator(n, e, array, cmp); + size = n + 1; notEmpty.signal(); - return true; } finally { lock.unlock(); } + return true; } /** - * Inserts the specified element into this priority queue. As the queue is - * unbounded this method will never block. + * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never block. * * @param e the element to add * @throws ClassCastException if the specified element cannot be compared @@ -213,13 +498,15 @@ } /** - * Inserts the specified element into this priority queue. As the queue is - * unbounded this method will never block. + * Inserts the specified element into this priority queue. + * As the queue is unbounded, this method will never block or + * return {@code false}. * * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks - * @return <tt>true</tt> + * @return {@code true} (as specified by + * {@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 @@ -232,95 +519,121 @@ public E poll() { final ReentrantLock lock = this.lock; lock.lock(); + E result; try { - return q.poll(); + result = extract(); } finally { lock.unlock(); } + return result; } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); + E result; try { - try { - while (q.size() == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = q.poll(); - assert x != null; - return x; + while ( (result = extract()) == null) + notEmpty.await(); } finally { lock.unlock(); } + return result; } public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); + E result; try { - for (;;) { - E x = q.poll(); - if (x != null) - return x; - if (nanos <= 0) - return null; - try { - nanos = notEmpty.awaitNanos(nanos); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - } + while ( (result = extract()) == null && nanos > 0) + nanos = notEmpty.awaitNanos(nanos); } finally { lock.unlock(); } + return result; } public E peek() { final ReentrantLock lock = this.lock; lock.lock(); + E result; try { - return q.peek(); + result = size > 0 ? (E) queue[0] : null; + } finally { + lock.unlock(); + } + return result; + } + + /** + * Returns the comparator used to order the elements in this queue, + * or {@code null} if this queue uses the {@linkplain Comparable + * natural ordering} of its elements. + * + * @return the comparator used to order the elements in this queue, + * or {@code null} if this queue uses the natural + * ordering of its elements + */ + public Comparator<? super E> comparator() { + return comparator; + } + + public int size() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return size; } finally { lock.unlock(); } } /** - * Returns the comparator used to order the elements in this queue, - * or <tt>null</tt> if this queue uses the {@linkplain Comparable - * natural ordering} of its elements. - * - * @return the comparator used to order the elements in this queue, - * or <tt>null</tt> if this queue uses the natural - * ordering of its elements + * Always returns {@code Integer.MAX_VALUE} because + * a {@code PriorityBlockingQueue} is not capacity constrained. + * @return {@code Integer.MAX_VALUE} always */ - public Comparator<? super E> comparator() { - return q.comparator(); + public int remainingCapacity() { + return Integer.MAX_VALUE; } - public int size() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return q.size(); - } finally { - lock.unlock(); + private int indexOf(Object o) { + if (o != null) { + Object[] array = queue; + int n = size; + for (int i = 0; i < n; i++) + if (o.equals(array[i])) + return i; } + return -1; } /** - * Always returns <tt>Integer.MAX_VALUE</tt> because - * a <tt>PriorityBlockingQueue</tt> is not capacity constrained. - * @return <tt>Integer.MAX_VALUE</tt> + * Removes the ith element from queue. */ - public int remainingCapacity() { - return Integer.MAX_VALUE; + private void removeAt(int i) { + Object[] array = queue; + int n = size - 1; + if (n == i) // removed last element + array[i] = null; + else { + E moved = (E) array[n]; + array[n] = null; + Comparator<? super E> cmp = comparator; + if (cmp == null) + siftDownComparable(i, moved, array, n); + else + siftDownUsingComparator(i, moved, array, n, cmp); + if (array[i] == moved) { + if (cmp == null) + siftUpComparable(i, moved, array); + else + siftUpUsingComparator(i, moved, array, cmp); + } + } + size = n; } /** @@ -332,13 +645,40 @@ * result of the call). * * @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 + * @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 { - return q.remove(o); + int i = indexOf(o); + if (i != -1) { + removeAt(i); + removed = true; + } + } finally { + lock.unlock(); + } + return removed; + } + + + /** + * Identity-based version for use in Itr.remove + */ + private 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++) { + if (o == array[i]) { + removeAt(i); + break; + } + } } finally { lock.unlock(); } @@ -350,16 +690,18 @@ * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this queue - * @return <tt>true</tt> if this queue contains the specified element + * @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 { - return q.contains(o); + index = indexOf(o); } finally { lock.unlock(); } + return index != -1; } /** @@ -379,7 +721,7 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(); + return Arrays.copyOf(queue, size); } finally { lock.unlock(); } @@ -390,7 +732,18 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toString(); + int n = size; + if (n == 0) + return "[]"; + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < n; ++i) { + E e = (E)queue[i]; + sb.append(e == this ? "(this Collection)" : e); + if (i != n - 1) + sb.append(',').append(' '); + } + return sb.append(']').toString(); } finally { lock.unlock(); } @@ -412,7 +765,7 @@ try { int n = 0; E e; - while ( (e = q.poll()) != null) { + while ( (e = extract()) != null) { c.add(e); ++n; } @@ -440,7 +793,7 @@ try { int n = 0; E e; - while (n < maxElements && (e = q.poll()) != null) { + while (n < maxElements && (e = extract()) != null) { c.add(e); ++n; } @@ -458,7 +811,11 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - q.clear(); + Object[] array = queue; + int n = size; + size = 0; + for (int i = 0; i < n; i++) + array[i] = null; } finally { lock.unlock(); } @@ -475,22 +832,22 @@ * <p>If this queue fits in the specified array with room to spare * (i.e., the array has more elements than this queue), the element in * the array immediately following the end of the queue is set to - * <tt>null</tt>. + * {@code null}. * * <p>Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * - * <p>Suppose <tt>x</tt> is a queue known to contain only strings. + * <p>Suppose {@code x} is a queue known to contain only strings. * The following code can be used to dump the queue into a newly - * allocated array of <tt>String</tt>: + * allocated array of {@code String}: * * <pre> * String[] y = x.toArray(new String[0]);</pre> * - * Note that <tt>toArray(new Object[0])</tt> is identical in function to - * <tt>toArray()</tt>. + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. * * @param a the array into which the elements of the queue are to * be stored, if it is big enough; otherwise, a new array of the @@ -505,7 +862,14 @@ final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(a); + int n = size; + if (a.length < n) + // Make a new array of a's runtime type, but my contents: + return (T[]) Arrays.copyOf(queue, size, a.getClass()); + System.arraycopy(queue, 0, a, 0, n); + if (a.length > n) + a[n] = null; + return a; } finally { lock.unlock(); } @@ -514,8 +878,9 @@ /** * Returns an iterator over the elements in this queue. The * iterator does not return the elements in any particular order. - * The returned <tt>Iterator</tt> is a "weakly consistent" - * iterator that will never throw {@link + * + * <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 @@ -530,7 +895,7 @@ /** * Snapshot iterator that works off copy of underlying q array. */ - private class Itr implements Iterator<E> { + final class Itr implements Iterator<E> { final Object[] array; // Array of all elements int cursor; // index of next element to return; int lastRet; // index of last element, or -1 if no such @@ -554,39 +919,65 @@ 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(); - } } } /** - * Saves the state to a stream (that is, serializes it). This - * merely wraps default serialization within lock. The - * serialization strategy for items is left to underlying - * Queue. Note that locking is not needed on deserialization, so - * readObject is not defined, just relying on default. + * Saves the state to a stream (that is, serializes it). For + * compatibility with previous version of this class, + * elements are first copied to a java.util.PriorityQueue, + * which is then serialized. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { lock.lock(); try { + int n = size; // avoid zero capacity argument + q = new PriorityQueue<E>(n == 0 ? 1 : n, comparator); + q.addAll(this); s.defaultWriteObject(); } finally { + q = null; lock.unlock(); } } + /** + * Reconstitutes the {@code PriorityBlockingQueue} instance from a stream + * (that is, deserializes it). + * + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + try { + s.defaultReadObject(); + this.queue = new Object[q.size()]; + comparator = q.comparator(); + addAll(q); + } finally { + q = null; + } + } + + // 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) { + 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; + } + } + }
--- a/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Wed Feb 09 09:19:33 2011 -0500 +++ b/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Wed Feb 09 09:32:04 2011 -0500 @@ -360,8 +360,12 @@ getExecuteExistingDelayedTasksAfterShutdownPolicy(); boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy(); - if (!keepDelayed && !keepPeriodic) + if (!keepDelayed && !keepPeriodic) {