--- a/make/autoconf/toolchain_windows.m4 Thu Sep 12 15:04:00 2019 +0200
+++ b/make/autoconf/toolchain_windows.m4 Wed Sep 18 07:46:02 2019 +0200
@@ -808,7 +808,7 @@
if test "x$USE_UCRT" = "xtrue"; then
AC_MSG_CHECKING([for UCRT DLL dir])
if test "x$with_ucrt_dll_dir" != x; then
- if test -z "$(ls -d $with_ucrt_dll_dir/*.dll 2> /dev/null)"; then
+ if test -z "$(ls -d "$with_ucrt_dll_dir/"*.dll 2> /dev/null)"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([Could not find any dlls in $with_ucrt_dll_dir])
else
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/os/posix/gc/z/zUtils_posix.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2019, 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.
+ *
+ * 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.
+ */
+
+#include "precompiled.hpp"
+#include "gc/z/zUtils.hpp"
+#include "utilities/debug.hpp"
+
+#include <stdlib.h>
+
+uintptr_t ZUtils::alloc_aligned(size_t alignment, size_t size) {
+ void* res = NULL;
+
+ if (posix_memalign(&res, alignment, size) != 0) {
+ fatal("posix_memalign() failed");
+ }
+
+ memset(res, 0, size);
+
+ return (uintptr_t)res;
+}
--- a/src/hotspot/os/posix/os_posix.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/os/posix/os_posix.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -640,61 +640,6 @@
return;
}
-////////////////////////////////////////////////////////////////////////////////
-// interrupt support
-
-void os::interrupt(Thread* thread) {
- debug_only(Thread::check_for_dangling_thread_pointer(thread);)
- assert(thread->is_Java_thread(), "invariant");
- JavaThread* jt = (JavaThread*) thread;
- OSThread* osthread = thread->osthread();
-
- if (!osthread->interrupted()) {
- osthread->set_interrupted(true);
- // More than one thread can get here with the same value of osthread,
- // resulting in multiple notifications. We do, however, want the store
- // to interrupted() to be visible to other threads before we execute unpark().
- OrderAccess::fence();
- ParkEvent * const slp = jt->_SleepEvent ;
- if (slp != NULL) slp->unpark() ;
- }
-
- // For JSR166. Unpark even if interrupt status already was set
- jt->parker()->unpark();
-
- ParkEvent * ev = thread->_ParkEvent ;
- if (ev != NULL) ev->unpark() ;
-}
-
-bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
- debug_only(Thread::check_for_dangling_thread_pointer(thread);)
-
- OSThread* osthread = thread->osthread();
-
- bool interrupted = osthread->interrupted();
-
- // NOTE that since there is no "lock" around the interrupt and
- // is_interrupted operations, there is the possibility that the
- // interrupted flag (in osThread) will be "false" but that the
- // low-level events will be in the signaled state. This is
- // intentional. The effect of this is that Object.wait() and
- // LockSupport.park() will appear to have a spurious wakeup, which
- // is allowed and not harmful, and the possibility is so rare that
- // it is not worth the added complexity to add yet another lock.
- // For the sleep event an explicit reset is performed on entry
- // to JavaThread::sleep, so there is no early return. It has also been
- // recommended not to put the interrupted flag into the "event"
- // structure because it hides the issue.
- if (interrupted && clear_interrupted) {
- osthread->set_interrupted(false);
- // consider thread->_SleepEvent->reset() ... optional optimization
- }
-
- return interrupted;
-}
-
-
-
static const struct {
int sig; const char* name;
}
@@ -2107,7 +2052,7 @@
// Optional optimization -- avoid state transitions if there's
// an interrupt pending.
- if (Thread::is_interrupted(thread, false)) {
+ if (jt->is_interrupted(false)) {
return;
}
@@ -2130,7 +2075,7 @@
// Don't wait if cannot get lock since interference arises from
// unparking. Also re-check interrupt before trying wait.
- if (Thread::is_interrupted(thread, false) ||
+ if (jt->is_interrupted(false) ||
pthread_mutex_trylock(_mutex) != 0) {
return;
}
--- a/src/hotspot/os/solaris/os_solaris.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/os/solaris/os_solaris.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -5063,7 +5063,7 @@
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
- if (Thread::is_interrupted(thread, false)) {
+ if (jt->is_interrupted(false)) {
return;
}
@@ -5088,7 +5088,7 @@
// Don't wait if cannot get lock since interference arises from
// unblocking. Also. check interrupt before trying wait
- if (Thread::is_interrupted(thread, false) ||
+ if (jt->is_interrupted(false) ||
os::Solaris::mutex_trylock(_mutex) != 0) {
return;
}
--- a/src/hotspot/os/windows/osThread_windows.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/os/windows/osThread_windows.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -23,12 +23,9 @@
*/
// no precompiled headers
-#include "runtime/handles.inline.hpp"
-#include "runtime/mutexLocker.hpp"
+#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "runtime/osThread.hpp"
-#include "runtime/safepoint.hpp"
-#include "runtime/vmThread.hpp"
void OSThread::pd_initialize() {
set_thread_handle(NULL);
@@ -36,8 +33,34 @@
set_interrupt_event(NULL);
}
-// TODO: this is not well encapsulated; creation and deletion of the
-// interrupt_event are done in os_win32.cpp, create_thread and
-// free_thread. Should follow pattern of Linux/Solaris code here.
void OSThread::pd_destroy() {
+ if (_interrupt_event != NULL) {
+ CloseHandle(_interrupt_event);
+ }
}
+
+// We need to specialize these to interact with the _interrupt_event.
+
+volatile bool OSThread::interrupted() {
+ return _interrupted != 0 &&
+ (WaitForSingleObject(_interrupt_event, 0) == WAIT_OBJECT_0);
+}
+
+void OSThread::set_interrupted(bool z) {
+ if (z) {
+ _interrupted = 1;
+ // More than one thread can get here with the same value of osthread,
+ // resulting in multiple notifications. We do, however, want the store
+ // to interrupted() to be visible to other threads before we post
+ // the interrupt event.
+ OrderAccess::release();
+ SetEvent(_interrupt_event);
+ }
+ else {
+ // We should only ever clear the interrupt if we are in fact interrupted,
+ // and this can only be done by the current thread on itself.
+ assert(_interrupted == 1, "invariant for clearing interrupt state");
+ _interrupted = 0;
+ ResetEvent(_interrupt_event);
+ }
+}
--- a/src/hotspot/os/windows/osThread_windows.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/os/windows/osThread_windows.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -32,7 +32,8 @@
private:
// Win32-specific thread information
HANDLE _thread_handle; // Win32 thread handle
- HANDLE _interrupt_event; // Event signalled on thread interrupt
+ HANDLE _interrupt_event; // Event signalled on thread interrupt for use by
+ // Process.waitFor().
ThreadState _last_state;
public:
@@ -42,6 +43,11 @@
void set_thread_handle(HANDLE handle) { _thread_handle = handle; }
HANDLE interrupt_event() const { return _interrupt_event; }
void set_interrupt_event(HANDLE interrupt_event) { _interrupt_event = interrupt_event; }
+ // These are specialized on Windows to interact with the _interrupt_event.
+ // Also note that Windows does not skip these calls if we are interrupted - see
+ // LibraryCallKit::inline_native_isInterrupted
+ volatile bool interrupted();
+ void set_interrupted(bool z);
#ifndef PRODUCT
// Used for debugging, return a unique integer for each thread.
@@ -54,7 +60,6 @@
return false;
}
#endif // ASSERT
- bool is_try_mutex_enter() { return false; }
// This is a temporary fix for the thread states during
// suspend/resume until we throw away OSThread completely.
--- a/src/hotspot/os/windows/os_windows.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/os/windows/os_windows.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -612,7 +612,9 @@
return false;
}
osthread->set_interrupt_event(interrupt_event);
- osthread->set_interrupted(false);
+ // We don't call set_interrupted(false) as it will trip the assert in there
+ // as we are not operating on the current thread. We don't need to call it
+ // because the initial state is already correct.
thread->set_osthread(osthread);
@@ -684,7 +686,6 @@
if (thread_handle == NULL) {
// Need to clean up stuff we've allocated so far
- CloseHandle(osthread->interrupt_event());
thread->set_osthread(NULL);
delete osthread;
return false;
@@ -714,7 +715,6 @@
"os::free_thread but not current thread");
CloseHandle(osthread->thread_handle());
- CloseHandle(osthread->interrupt_event());
delete osthread;
}
@@ -3485,7 +3485,6 @@
}
-
// Short sleep, direct OS call.
//
// ms = 0, means allow others (if any) to run.
@@ -3593,49 +3592,6 @@
return OS_OK;
}
-void os::interrupt(Thread* thread) {
- debug_only(Thread::check_for_dangling_thread_pointer(thread);)
- assert(thread->is_Java_thread(), "invariant");
- JavaThread* jt = (JavaThread*) thread;
- OSThread* osthread = thread->osthread();
- osthread->set_interrupted(true);
- // More than one thread can get here with the same value of osthread,
- // resulting in multiple notifications. We do, however, want the store
- // to interrupted() to be visible to other threads before we post
- // the interrupt event.
- OrderAccess::release();
- SetEvent(osthread->interrupt_event());
- // For JSR166: unpark after setting status
- jt->parker()->unpark();
-
- ParkEvent * ev = thread->_ParkEvent;
- if (ev != NULL) ev->unpark();
-
- ev = jt->_SleepEvent;
- if (ev != NULL) ev->unpark();
-}
-
-
-bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
- debug_only(Thread::check_for_dangling_thread_pointer(thread);)
-
- OSThread* osthread = thread->osthread();
- // There is no synchronization between the setting of the interrupt
- // and it being cleared here. It is critical - see 6535709 - that
- // we only clear the interrupt state, and reset the interrupt event,
- // if we are going to report that we were indeed interrupted - else
- // an interrupt can be "lost", leading to spurious wakeups or lost wakeups
- // depending on the timing. By checking thread interrupt event to see
- // if the thread gets real interrupt thus prevent spurious wakeup.
- bool interrupted = osthread->interrupted() && (WaitForSingleObject(osthread->interrupt_event(), 0) == WAIT_OBJECT_0);
- if (interrupted && clear_interrupted) {
- osthread->set_interrupted(false);
- ResetEvent(osthread->interrupt_event());
- } // Otherwise leave the interrupted state alone
-
- return interrupted;
-}
-
// GetCurrentThreadId() returns DWORD
intx os::current_thread_id() { return GetCurrentThreadId(); }
@@ -5346,7 +5302,7 @@
JavaThread* thread = JavaThread::current();
// Don't wait if interrupted or already triggered
- if (Thread::is_interrupted(thread, false) ||
+ if (thread->is_interrupted(false) ||
WaitForSingleObject(_ParkEvent, 0) == WAIT_OBJECT_0) {
ResetEvent(_ParkEvent);
return;
--- a/src/hotspot/share/ci/ciEnv.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/ci/ciEnv.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -539,7 +539,7 @@
// Calculate accessibility the hard way.
if (!k->is_loaded()) {
is_accessible = false;
- } else if (!oopDesc::equals(k->loader(), accessor->loader()) &&
+ } else if (k->loader() != accessor->loader() &&
get_klass_by_name_impl(accessor, cpool, k->name(), true) == NULL) {
// Loaded only remotely. Not linked yet.
is_accessible = false;
@@ -590,7 +590,7 @@
index = cpool->object_to_cp_index(cache_index);
oop obj = cpool->resolved_references()->obj_at(cache_index);
if (obj != NULL) {
- if (oopDesc::equals(obj, Universe::the_null_sentinel())) {
+ if (obj == Universe::the_null_sentinel()) {
return ciConstant(T_OBJECT, get_object(NULL));
}
BasicType bt = T_OBJECT;
--- a/src/hotspot/share/ci/ciObjectFactory.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/ci/ciObjectFactory.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -250,7 +250,7 @@
// into the cache.
Handle keyHandle(Thread::current(), key);
ciObject* new_object = create_new_object(keyHandle());
- assert(oopDesc::equals(keyHandle(), new_object->get_oop()), "must be properly recorded");
+ assert(keyHandle() == new_object->get_oop(), "must be properly recorded");
init_ident_of(new_object);
assert(Universe::heap()->is_in(new_object->get_oop()), "must be");
@@ -469,8 +469,8 @@
for (int i=0; i<_unloaded_klasses->length(); i++) {
ciKlass* entry = _unloaded_klasses->at(i);
if (entry->name()->equals(name) &&
- oopDesc::equals(entry->loader(), loader) &&
- oopDesc::equals(entry->protection_domain(), domain)) {
+ entry->loader() == loader &&
+ entry->protection_domain() == domain) {
// We've found a match.
return entry;
}
--- a/src/hotspot/share/ci/ciObjectFactory.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/ci/ciObjectFactory.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -74,7 +74,7 @@
ciMetadata* create_new_metadata(Metadata* o);
static bool is_equal(NonPermObject* p, oop key) {
- return oopDesc::equals(p->object()->get_oop(), key);
+ return p->object()->get_oop() == key;
}
NonPermObject* &find_non_perm(oop key);
--- a/src/hotspot/share/classfile/classLoader.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/classLoader.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1325,7 +1325,7 @@
THREAD);
if (HAS_PENDING_EXCEPTION) {
if (DumpSharedSpaces) {
- tty->print_cr("Preload Error: Failed to load %s", class_name);
+ log_error(cds)("Preload Error: Failed to load %s", class_name);
}
return NULL;
}
--- a/src/hotspot/share/classfile/classLoaderData.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/classLoaderData.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -236,7 +236,7 @@
VerifyContainsOopClosure(oop target) : _target(target), _found(false) {}
void do_oop(oop* p) {
- if (p != NULL && oopDesc::equals(NativeAccess<AS_NO_KEEPALIVE>::oop_load(p), _target)) {
+ if (p != NULL && NativeAccess<AS_NO_KEEPALIVE>::oop_load(p) == _target) {
_found = true;
}
}
@@ -437,7 +437,7 @@
// Just return if this dependency is to a class with the same or a parent
// class_loader.
- if (oopDesc::equals(from, to) || java_lang_ClassLoader::isAncestor(from, to)) {
+ if (from == to || java_lang_ClassLoader::isAncestor(from, to)) {
return; // this class loader is in the parent list, no need to add it.
}
}
--- a/src/hotspot/share/classfile/classLoaderExt.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/classLoaderExt.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -32,6 +32,7 @@
#include "classfile/modules.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp"
+#include "logging/log.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/filemap.hpp"
#include "memory/resourceArea.hpp"
@@ -146,7 +147,7 @@
if (found != NULL) {
// Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java
// If duplicated entries are found, the last one is used.
- tty->print_cr("Warning: Duplicate name in Manifest: %s.\n"
+ log_warning(cds)("Warning: Duplicate name in Manifest: %s.\n"
"Ensure that the manifest does not have duplicate entries, and\n"
"that blank lines separate individual sections in both your\n"
"manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n", tag, jar_path);
@@ -276,7 +277,7 @@
}
if (NULL == stream) {
- tty->print_cr("Preload Warning: Cannot find %s", class_name);
+ log_warning(cds)("Preload Warning: Cannot find %s", class_name);
return NULL;
}
@@ -295,7 +296,7 @@
THREAD);
if (HAS_PENDING_EXCEPTION) {
- tty->print_cr("Preload Error: Failed to load %s", class_name);
+ log_error(cds)("Preload Error: Failed to load %s", class_name);
return NULL;
}
return result;
--- a/src/hotspot/share/classfile/classLoaderStats.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/classLoaderStats.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -98,7 +98,7 @@
class ClassLoaderStatsClosure : public CLDClosure {
protected:
static bool oop_equals(oop const& s1, oop const& s2) {
- return oopDesc::equals(s1, s2);
+ return s1 == s2;
}
static unsigned oop_hash(oop const& s1) {
--- a/src/hotspot/share/classfile/dictionary.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/dictionary.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -153,13 +153,13 @@
// a Dictionary entry, which can be moved if the Dictionary is resized.
MutexLocker ml(ProtectionDomainSet_lock, Mutex::_no_safepoint_check_flag);
#ifdef ASSERT
- if (oopDesc::equals(protection_domain, instance_klass()->protection_domain())) {
+ if (protection_domain == instance_klass()->protection_domain()) {
// Ensure this doesn't show up in the pd_set (invariant)
bool in_pd_set = false;
for (ProtectionDomainEntry* current = pd_set();
current != NULL;
current = current->next()) {
- if (oopDesc::equals(current->object_no_keepalive(), protection_domain)) {
+ if (current->object_no_keepalive() == protection_domain) {
in_pd_set = true;
break;
}
@@ -171,7 +171,7 @@
}
#endif /* ASSERT */
- if (oopDesc::equals(protection_domain, instance_klass()->protection_domain())) {
+ if (protection_domain == instance_klass()->protection_domain()) {
// Succeeds trivially
return true;
}
@@ -179,7 +179,7 @@
for (ProtectionDomainEntry* current = pd_set();
current != NULL;
current = current->next()) {
- if (oopDesc::equals(current->object_no_keepalive(), protection_domain)) return true;
+ if (current->object_no_keepalive() == protection_domain) return true;
}
return false;
}
--- a/src/hotspot/share/classfile/javaClasses.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/javaClasses.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -878,7 +878,7 @@
} else {
assert(Universe::is_module_initialized() ||
(ModuleEntryTable::javabase_defined() &&
- (oopDesc::equals(module(), ModuleEntryTable::javabase_moduleEntry()->module()))),
+ (module() == ModuleEntryTable::javabase_moduleEntry()->module())),
"Incorrect java.lang.Module specification while creating mirror");
set_module(mirror(), module());
}
@@ -955,7 +955,7 @@
}
// set the classLoader field in the java_lang_Class instance
- assert(oopDesc::equals(class_loader(), k->class_loader()), "should be same");
+ assert(class_loader() == k->class_loader(), "should be same");
set_class_loader(mirror(), class_loader());
// Setup indirection from klass->mirror
@@ -1510,9 +1510,9 @@
// Note: create_basic_type_mirror above initializes ak to a non-null value.
type = ArrayKlass::cast(ak)->element_type();
} else {
- assert(oopDesc::equals(java_class, Universe::void_mirror()), "only valid non-array primitive");
+ assert(java_class == Universe::void_mirror(), "only valid non-array primitive");
}
- assert(oopDesc::equals(Universe::java_mirror(type), java_class), "must be consistent");
+ assert(Universe::java_mirror(type) == java_class, "must be consistent");
return type;
}
@@ -3712,14 +3712,14 @@
}
bool java_lang_invoke_MethodType::equals(oop mt1, oop mt2) {
- if (oopDesc::equals(mt1, mt2))
+ if (mt1 == mt2)
return true;
- if (!oopDesc::equals(rtype(mt1), rtype(mt2)))
+ if (rtype(mt1) != rtype(mt2))
return false;
if (ptype_count(mt1) != ptype_count(mt2))
return false;
for (int i = ptype_count(mt1) - 1; i >= 0; i--) {
- if (!oopDesc::equals(ptype(mt1, i), ptype(mt2, i)))
+ if (ptype(mt1, i) != ptype(mt2, i))
return false;
}
return true;
@@ -3933,7 +3933,7 @@
// This loop taken verbatim from ClassLoader.java:
do {
acl = parent(acl);
- if (oopDesc::equals(cl, acl)) {
+ if (cl == acl) {
return true;
}
assert(++loop_count > 0, "loop_count overflow");
@@ -3963,7 +3963,7 @@
oop cl = SystemDictionary::java_system_loader();
while(cl != NULL) {
- if (oopDesc::equals(cl, loader)) return true;
+ if (cl == loader) return true;
cl = parent(cl);
}
return false;
--- a/src/hotspot/share/classfile/javaClasses.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/javaClasses.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -52,7 +52,7 @@
// Accessors
bool java_lang_String::value_equals(typeArrayOop str_value1, typeArrayOop str_value2) {
- return (oopDesc::equals(str_value1, str_value2) ||
+ return ((str_value1 == str_value2) ||
(str_value1->length() == str_value2->length() &&
(!memcmp(str_value1->base(T_BYTE),
str_value2->base(T_BYTE),
--- a/src/hotspot/share/classfile/modules.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/modules.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -306,7 +306,7 @@
oop loader = java_lang_Module::loader(module_handle());
// Make sure loader is not the jdk.internal.reflect.DelegatingClassLoader.
- if (!oopDesc::equals(loader, java_lang_ClassLoader::non_reflection_class_loader(loader))) {
+ if (loader != java_lang_ClassLoader::non_reflection_class_loader(loader)) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
"Class loader is an invalid delegating class loader");
}
--- a/src/hotspot/share/classfile/protectionDomainCache.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/protectionDomainCache.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -160,7 +160,7 @@
ProtectionDomainCacheEntry* ProtectionDomainCacheTable::find_entry(int index, Handle protection_domain) {
assert_locked_or_safepoint(SystemDictionary_lock);
for (ProtectionDomainCacheEntry* e = bucket(index); e != NULL; e = e->next()) {
- if (oopDesc::equals(e->object_no_keepalive(), protection_domain())) {
+ if (e->object_no_keepalive() == protection_domain()) {
return e;
}
}
--- a/src/hotspot/share/classfile/systemDictionary.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -177,7 +177,7 @@
return false;
}
return (class_loader->klass() == SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass() ||
- oopDesc::equals(class_loader, _java_system_loader));
+ class_loader == _java_system_loader);
}
// Returns true if the passed class loader is the platform class loader.
@@ -393,7 +393,7 @@
if ((childk != NULL ) && (is_superclass) &&
((quicksuperk = childk->java_super()) != NULL) &&
((quicksuperk->name() == super_name) &&
- (oopDesc::equals(quicksuperk->class_loader(), class_loader())))) {
+ (quicksuperk->class_loader() == class_loader()))) {
return quicksuperk;
} else {
PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, child_name, loader_data);
@@ -542,7 +542,7 @@
bool calledholdinglock
= ObjectSynchronizer::current_thread_holds_lock((JavaThread*)THREAD, lockObject);
assert(calledholdinglock,"must hold lock for notify");
- assert((!oopDesc::equals(lockObject(), _system_loader_lock_obj) && !is_parallelCapable(lockObject)), "unexpected double_lock_wait");
+ assert((lockObject() != _system_loader_lock_obj && !is_parallelCapable(lockObject)), "unexpected double_lock_wait");
ObjectSynchronizer::notifyall(lockObject, THREAD);
intptr_t recursions = ObjectSynchronizer::complete_exit(lockObject, THREAD);
SystemDictionary_lock->wait();
@@ -850,7 +850,7 @@
// If everything was OK (no exceptions, no null return value), and
// class_loader is NOT the defining loader, do a little more bookkeeping.
if (!HAS_PENDING_EXCEPTION && k != NULL &&
- !oopDesc::equals(k->class_loader(), class_loader())) {
+ k->class_loader() != class_loader()) {
check_constraints(d_hash, k, class_loader, false, THREAD);
@@ -1003,7 +1003,7 @@
if (unsafe_anonymous_host != NULL) {
// Create a new CLD for an unsafe anonymous class, that uses the same class loader
// as the unsafe_anonymous_host
- guarantee(oopDesc::equals(unsafe_anonymous_host->class_loader(), class_loader()), "should be the same");
+ guarantee(unsafe_anonymous_host->class_loader() == class_loader(), "should be the same");
loader_data = ClassLoaderData::unsafe_anonymous_class_loader_data(class_loader);
} else {
loader_data = ClassLoaderData::class_loader_data(class_loader());
@@ -1729,7 +1729,7 @@
== ObjectSynchronizer::owner_other) {
// contention will likely happen, so increment the corresponding
// contention counter.
- if (oopDesc::equals(loader_lock(), _system_loader_lock_obj)) {
+ if (loader_lock() == _system_loader_lock_obj) {
ClassLoader::sync_systemLoaderLockContentionRate()->inc();
} else {
ClassLoader::sync_nonSystemLoaderLockContentionRate()->inc();
@@ -2150,7 +2150,7 @@
// cleared if revocation occurs too often for this type
// NOTE that we must only do this when the class is initally
// defined, not each time it is referenced from a new class loader
- if (oopDesc::equals(k->class_loader(), class_loader())) {
+ if (k->class_loader() == class_loader()) {
k->set_prototype_header(markWord::biased_locking_prototype());
}
}
@@ -2343,7 +2343,7 @@
Handle loader1, Handle loader2,
bool is_method, TRAPS) {
// Nothing to do if loaders are the same.
- if (oopDesc::equals(loader1(), loader2())) {
+ if (loader1() == loader2()) {
return NULL;
}
--- a/src/hotspot/share/code/dependencies.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/code/dependencies.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1813,12 +1813,12 @@
if (changes == NULL) {
// Validate all CallSites
- if (!oopDesc::equals(java_lang_invoke_CallSite::target(call_site), method_handle))
+ if (java_lang_invoke_CallSite::target(call_site) != method_handle)
return call_site->klass(); // assertion failed
} else {
// Validate the given CallSite
- if (oopDesc::equals(call_site, changes->call_site()) && !oopDesc::equals(java_lang_invoke_CallSite::target(call_site), changes->method_handle())) {
- assert(!oopDesc::equals(method_handle, changes->method_handle()), "must be");
+ if (call_site == changes->call_site() && java_lang_invoke_CallSite::target(call_site) != changes->method_handle()) {
+ assert(method_handle != changes->method_handle(), "must be");
return call_site->klass(); // assertion failed
}
}
--- a/src/hotspot/share/compiler/compileBroker.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/compiler/compileBroker.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -316,7 +316,7 @@
// We only allow the last compiler thread of each type to get removed.
jobject last_compiler = c1 ? CompileBroker::compiler1_object(compiler_count - 1)
: CompileBroker::compiler2_object(compiler_count - 1);
- if (oopDesc::equals(ct->threadObj(), JNIHandles::resolve_non_null(last_compiler))) {
+ if (ct->threadObj() == JNIHandles::resolve_non_null(last_compiler)) {
if (do_it) {
assert_locked_or_safepoint(CompileThread_lock); // Update must be consistent.
compiler->set_num_compiler_threads(compiler_count - 1);
@@ -557,8 +557,14 @@
}
void CompileQueue::print_tty() {
- ttyLocker ttyl;
- print(tty);
+ ResourceMark rm;
+ stringStream ss;
+ // Dump the compile queue into a buffer before locking the tty
+ print(&ss);
+ {
+ ttyLocker ttyl;
+ tty->print("%s", ss.as_string());
+ }
}
CompilerCounters::CompilerCounters() {
@@ -1687,7 +1693,7 @@
int compiler_number = 0;
bool found = false;
for (; compiler_number < count; compiler_number++) {
- if (oopDesc::equals(JNIHandles::resolve_non_null(compiler_objects[compiler_number]), compiler_obj)) {
+ if (JNIHandles::resolve_non_null(compiler_objects[compiler_number]) == compiler_obj) {
found = true;
break;
}
--- a/src/hotspot/share/gc/shared/barrierSet.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shared/barrierSet.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -144,10 +144,6 @@
virtual void make_parsable(JavaThread* thread) {}
-#ifdef CHECK_UNHANDLED_OOPS
- virtual bool oop_equals_operator_allowed() { return true; }
-#endif
-
public:
// Print a description of the memory for the barrier set
virtual void print_on(outputStream* st) const = 0;
@@ -318,10 +314,6 @@
static oop resolve(oop obj) {
return Raw::resolve(obj);
}
-
- static bool equals(oop o1, oop o2) {
- return Raw::equals(o1, o2);
- }
};
};
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -175,7 +175,7 @@
}
bool CollectedHeap::is_oop(oop object) const {
- if (!check_obj_alignment(object)) {
+ if (!is_object_aligned(object)) {
return false;
}
@@ -345,7 +345,7 @@
#endif // PRODUCT
void CollectedHeap::check_oop_location(void* addr) const {
- assert(check_obj_alignment(addr), "address is not aligned");
+ assert(is_object_aligned(addr), "address is not aligned");
assert(_reserved.contains(addr), "address is not in reserved heap");
}
--- a/src/hotspot/share/gc/shared/referenceProcessor.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -282,7 +282,7 @@
// First _prev_next ref actually points into DiscoveredList (gross).
oop new_next;
- if (oopDesc::equals_raw(_next_discovered, _current_discovered)) {
+ if (_next_discovered == _current_discovered) {
// At the end of the list, we should make _prev point to itself.
// If _ref is the first ref, then _prev_next will be in the DiscoveredList,
// and _prev will be NULL.
@@ -472,7 +472,7 @@
ReferenceProcessor::clear_discovered_references(DiscoveredList& refs_list) {
oop obj = NULL;
oop next = refs_list.head();
- while (!oopDesc::equals_raw(next, obj)) {
+ while (next != obj) {
obj = next;
next = java_lang_ref_Reference::discovered(obj);
java_lang_ref_Reference::set_discovered_raw(obj, NULL);
@@ -744,7 +744,7 @@
ref_lists[to_idx].inc_length(refs_to_move);
// Remove the chain from the from list.
- if (oopDesc::equals_raw(move_tail, new_head)) {
+ if (move_tail == new_head) {
// We found the end of the from list.
ref_lists[from_idx].set_head(NULL);
} else {
--- a/src/hotspot/share/gc/shared/referenceProcessor.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -143,13 +143,13 @@
inline size_t removed() const { return _removed; }
inline void move_to_next() {
- if (oopDesc::equals_raw(_current_discovered, _next_discovered)) {
+ if (_current_discovered == _next_discovered) {
// End of the list.
_current_discovered = NULL;
} else {
_current_discovered = _next_discovered;
}
- assert(!oopDesc::equals_raw(_current_discovered, _first_seen), "cyclic ref_list found");
+ assert(_current_discovered != _first_seen, "cyclic ref_list found");
_processed++;
}
};
--- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -363,7 +363,7 @@
}
typeArrayOop existing_value = lookup_or_add(value, latin1, hash);
- if (oopDesc::equals_raw(existing_value, value)) {
+ if (existing_value == value) {
// Same value, already known
stat->inc_known();
return;
--- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -142,7 +142,7 @@
if (level >= _safe_oop) {
oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
msg.append("Forwardee:\n");
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
if (level >= _safe_oop_fwd) {
print_obj(msg, fwd);
} else {
@@ -157,7 +157,7 @@
if (level >= _safe_oop_fwd) {
oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
oop fwd2 = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(fwd);
- if (!oopDesc::equals_raw(fwd, fwd2)) {
+ if (fwd != fwd2) {
msg.append("Second forwardee:\n");
print_obj_safe(msg, fwd2);
msg.append("\n");
@@ -203,7 +203,7 @@
oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj));
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
// When Full GC moves the objects, we cannot trust fwdptrs. If we got here, it means something
// tries fwdptr manipulation when Full GC is running. The only exception is using the fwdptr
// that still points to the object itself.
@@ -235,7 +235,7 @@
// Step 4. Check for multiple forwardings
oop fwd2 = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(fwd));
- if (!oopDesc::equals_raw(fwd, fwd2)) {
+ if (fwd != fwd2) {
print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_correct failed",
"Multiple forwardings",
file, line);
@@ -278,7 +278,7 @@
assert_correct(interior_loc, obj, file, line);
oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj));
- if (oopDesc::equals_raw(obj, fwd)) {
+ if (obj == fwd) {
print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_forwarded failed",
"Object should be forwarded",
file, line);
@@ -289,7 +289,7 @@
assert_correct(interior_loc, obj, file, line);
oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj));
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_not_forwarded failed",
"Object should not be forwarded",
file, line);
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -238,7 +238,7 @@
shenandoah_assert_in_cset(NULL, obj);
oop fwd = resolve_forwarded_not_null(obj);
- if (oopDesc::equals_raw(obj, fwd)) {
+ if (obj == fwd) {
ShenandoahEvacOOMScope oom_evac_scope;
Thread* thread = Thread::current();
@@ -267,7 +267,7 @@
size_t count = 0;
while ((cur < r->top()) && ctx->is_marked(oop(cur)) && (count++ < max)) {
oop cur_oop = oop(cur);
- if (oopDesc::equals_raw(cur_oop, resolve_forwarded_not_null(cur_oop))) {
+ if (cur_oop == resolve_forwarded_not_null(cur_oop)) {
_heap->evacuate_object(cur_oop, thread);
}
cur = cur + cur_oop->size();
@@ -286,7 +286,7 @@
oop fwd = resolve_forwarded_not_null(obj);
if (evac_in_progress &&
_heap->in_collection_set(obj) &&
- oopDesc::equals_raw(obj, fwd)) {
+ obj == fwd) {
Thread *t = Thread::current();
if (t->is_GC_task_thread()) {
return _heap->evacuate_object(obj, t);
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -103,7 +103,7 @@
compare_value = expected;
res = Raw::oop_atomic_cmpxchg(new_value, addr, compare_value);
expected = res;
- } while ((! oopDesc::equals_raw(compare_value, expected)) && oopDesc::equals_raw(resolve_forwarded(compare_value), resolve_forwarded(expected)));
+ } while ((compare_value != expected) && (resolve_forwarded(compare_value) == resolve_forwarded(expected)));
if (res != NULL) {
return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_not_null(res);
} else {
@@ -118,7 +118,7 @@
oop result = oop_atomic_cmpxchg_not_in_heap(new_value, addr, compare_value);
const bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0;
if (keep_alive && ShenandoahSATBBarrier && !CompressedOops::is_null(result) &&
- oopDesc::equals_raw(result, compare_value) &&
+ (result == compare_value) &&
ShenandoahHeap::heap()->is_concurrent_mark_in_progress()) {
ShenandoahBarrierSet::barrier_set()->enqueue(result);
}
@@ -307,7 +307,7 @@
case EVAC_BARRIER:
if (_heap->in_collection_set(obj)) {
oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (oopDesc::equals_raw(forw, obj)) {
+ if (forw == obj) {
forw = _heap->evacuate_object(forw, thread);
}
obj = forw;
--- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -92,7 +92,7 @@
if (_heap->in_collection_set(obj)) {
shenandoah_assert_marked(p, obj);
oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (oopDesc::equals_raw(resolved, obj)) {
+ if (resolved == obj) {
resolved = _heap->evacuate_object(obj, _thread);
}
RawAccess<IS_NOT_NULL>::oop_store(p, resolved);
@@ -119,7 +119,7 @@
if (_heap->in_collection_set(obj)) {
shenandoah_assert_marked(p, obj);
oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (oopDesc::equals_raw(resolved, obj)) {
+ if (resolved == obj) {
resolved = _heap->evacuate_object(obj, _thread);
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1922,7 +1922,7 @@
else if (prev == CANCELLED) return false;
assert(ShenandoahSuspendibleWorkers, "should not get here when not using suspendible workers");
assert(prev == NOT_CANCELLED, "must be NOT_CANCELLED");
- {
+ if (Thread::current()->is_Java_thread()) {
// We need to provide a safepoint here, otherwise we might
// spin forever if a SP is pending.
ThreadBlockInVM sp(JavaThread::current());
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -113,11 +113,11 @@
oop heap_oop = CompressedOops::decode_not_null(o);
if (in_collection_set(heap_oop)) {
oop forwarded_oop = ShenandoahBarrierSet::resolve_forwarded_not_null(heap_oop);
- if (oopDesc::equals_raw(forwarded_oop, heap_oop)) {
+ if (forwarded_oop == heap_oop) {
forwarded_oop = evacuate_object(heap_oop, Thread::current());
}
oop prev = cas_oop(forwarded_oop, p, heap_oop);
- if (oopDesc::equals_raw(prev, heap_oop)) {
+ if (prev == heap_oop) {
return forwarded_oop;
} else {
return NULL;
@@ -146,7 +146,7 @@
if (in_collection_set(heap_oop)) {
oop forwarded_oop = ShenandoahBarrierSet::resolve_forwarded_not_null(heap_oop);
- if (oopDesc::equals_raw(forwarded_oop, heap_oop)) {
+ if (forwarded_oop == heap_oop) {
// E.g. during evacuation.
return forwarded_oop;
}
@@ -159,7 +159,7 @@
// reference be updated later.
oop witness = cas_oop(forwarded_oop, p, heap_oop);
- if (!oopDesc::equals_raw(witness, heap_oop)) {
+ if (witness != heap_oop) {
// CAS failed, someone had beat us to it. Normally, we would return the failure witness,
// because that would be the proper write of to-space object, enforced by strong barriers.
// However, there is a corner case with arraycopy. It can happen that a Java thread
@@ -279,7 +279,7 @@
// Try to install the new forwarding pointer.
oop copy_val = oop(copy);
oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val);
- if (oopDesc::equals_raw(result, copy_val)) {
+ if (result == copy_val) {
// Successfully evacuated. Our copy is now the public one!
shenandoah_assert_correct(NULL, copy_val);
return copy_val;
--- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -141,11 +141,11 @@
public:
ObjArrayChunkedTask(oop o = NULL) {
- assert(oopDesc::equals_raw(decode_oop(encode_oop(o)), o), "oop can be encoded: " PTR_FORMAT, p2i(o));
+ assert(decode_oop(encode_oop(o)) == o, "oop can be encoded: " PTR_FORMAT, p2i(o));
_obj = encode_oop(o);
}
ObjArrayChunkedTask(oop o, int chunk, int pow) {
- assert(oopDesc::equals_raw(decode_oop(encode_oop(o)), o), "oop can be encoded: " PTR_FORMAT, p2i(o));
+ assert(decode_oop(encode_oop(o)) == o, "oop can be encoded: " PTR_FORMAT, p2i(o));
assert(decode_chunk(encode_chunk(chunk)) == chunk, "chunk can be encoded: %d", chunk);
assert(decode_pow(encode_pow(pow)) == pow, "pow can be encoded: %d", pow);
_obj = encode_oop(o) | encode_chunk(chunk) | encode_pow(pow);
--- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -672,7 +672,7 @@
if (!CompressedOops::is_null(o)) {
oop obj = CompressedOops::decode_not_null(o);
oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (!oopDesc::equals_raw(obj, forw)) {
+ if (obj != forw) {
RawAccess<IS_NOT_NULL>::oop_store(p, forw);
}
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -42,14 +42,14 @@
oop obj = CompressedOops::decode_not_null(o);
if (DEGEN) {
oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (!oopDesc::equals_raw(obj, forw)) {
+ if (obj != forw) {
// Update reference.
RawAccess<IS_NOT_NULL>::oop_store(p, forw);
}
obj = forw;
} else if (_heap->in_collection_set(obj)) {
oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj);
- if (oopDesc::equals_raw(obj, forw)) {
+ if (obj == forw) {
forw = _heap->evacuate_object(obj, thread);
}
shenandoah_assert_forwarded_except(p, obj, _heap->cancelled_gc());
--- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -35,6 +35,7 @@
#include "memory/iterator.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/compressedOops.inline.hpp"
+#include "utilities/align.hpp"
// Avoid name collision on verify_oop (defined in macroAssembler_arm.hpp)
#ifdef verify_oop
@@ -98,7 +99,7 @@
check(ShenandoahAsserts::_safe_unknown, obj, _heap->is_in(obj),
"oop must be in heap");
- check(ShenandoahAsserts::_safe_unknown, obj, check_obj_alignment(obj),
+ check(ShenandoahAsserts::_safe_unknown, obj, is_object_aligned(obj),
"oop must be aligned");
ShenandoahHeapRegion *obj_reg = _heap->heap_region_containing(obj);
@@ -153,12 +154,12 @@
ShenandoahHeapRegion* fwd_reg = NULL;
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
check(ShenandoahAsserts::_safe_oop, obj, _heap->is_in(fwd),
"Forwardee must be in heap");
check(ShenandoahAsserts::_safe_oop, obj, !CompressedOops::is_null(fwd),
"Forwardee is set");
- check(ShenandoahAsserts::_safe_oop, obj, check_obj_alignment(fwd),
+ check(ShenandoahAsserts::_safe_oop, obj, is_object_aligned(fwd),
"Forwardee must be aligned");
// Do this before touching fwd->size()
@@ -183,7 +184,7 @@
"Forwardee end should be within the region");
oop fwd2 = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(fwd);
- check(ShenandoahAsserts::_safe_oop, obj, oopDesc::equals_raw(fwd, fwd2),
+ check(ShenandoahAsserts::_safe_oop, obj, (fwd == fwd2),
"Double forwarding");
} else {
fwd_reg = obj_reg;
@@ -212,12 +213,12 @@
// skip
break;
case ShenandoahVerifier::_verify_forwarded_none: {
- check(ShenandoahAsserts::_safe_all, obj, oopDesc::equals_raw(obj, fwd),
+ check(ShenandoahAsserts::_safe_all, obj, (obj == fwd),
"Should not be forwarded");
break;
}
case ShenandoahVerifier::_verify_forwarded_allow: {
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
check(ShenandoahAsserts::_safe_all, obj, obj_reg != fwd_reg,
"Forwardee should be in another region");
}
@@ -237,7 +238,7 @@
break;
case ShenandoahVerifier::_verify_cset_forwarded:
if (_heap->in_collection_set(obj)) {
- check(ShenandoahAsserts::_safe_all, obj, !oopDesc::equals_raw(obj, fwd),
+ check(ShenandoahAsserts::_safe_all, obj, (obj != fwd),
"Object in collection set, should have forwardee");
}
break;
@@ -952,7 +953,7 @@
if (!CompressedOops::is_null(o)) {
oop obj = CompressedOops::decode_not_null(o);
oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, NULL,
"Verify Roots", "Should not be forwarded", __FILE__, __LINE__);
}
@@ -984,7 +985,7 @@
}
oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
- if (!oopDesc::equals_raw(obj, fwd)) {
+ if (obj != fwd) {
ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, NULL,
"Verify Roots In To-Space", "Should not be forwarded", __FILE__, __LINE__);
}
--- a/src/hotspot/share/gc/z/zCPU.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zCPU.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -33,8 +33,8 @@
#define ZCPU_UNKNOWN_SELF (Thread*)-2;
PaddedEnd<ZCPU::ZCPUAffinity>* ZCPU::_affinity = NULL;
-__thread Thread* ZCPU::_self = ZCPU_UNKNOWN_SELF;
-__thread uint32_t ZCPU::_cpu = 0;
+THREAD_LOCAL Thread* ZCPU::_self = ZCPU_UNKNOWN_SELF;
+THREAD_LOCAL uint32_t ZCPU::_cpu = 0;
void ZCPU::initialize() {
assert(_affinity == NULL, "Already initialized");
--- a/src/hotspot/share/gc/z/zCPU.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zCPU.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,6 +26,7 @@
#include "memory/allocation.hpp"
#include "memory/padded.hpp"
+#include "utilities/globalDefinitions.hpp"
class Thread;
@@ -36,8 +37,8 @@
};
static PaddedEnd<ZCPUAffinity>* _affinity;
- static __thread Thread* _self;
- static __thread uint32_t _cpu;
+ static THREAD_LOCAL Thread* _self;
+ static THREAD_LOCAL uint32_t _cpu;
public:
static void initialize();
--- a/src/hotspot/share/gc/z/zCollectedHeap.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -37,6 +37,7 @@
#include "memory/metaspace/metaspaceEnums.hpp"
#include "memory/universe.hpp"
#include "runtime/mutexLocker.hpp"
+#include "utilities/align.hpp"
ZCollectedHeap* ZCollectedHeap::heap() {
CollectedHeap* heap = Universe::heap();
@@ -371,7 +372,7 @@
}
void ZCollectedHeap::check_oop_location(void* addr) const {
- assert(check_obj_alignment(addr), "address is not aligned");
+ assert(is_object_aligned(addr), "address is not aligned");
const uintptr_t addr_int = reinterpret_cast<uintptr_t>(addr);
assert(addr_int >= ZAddressSpaceStart, "address is outside of the heap");
--- a/src/hotspot/share/gc/z/zLock.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zLock.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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,16 +25,13 @@
#define SHARE_GC_Z_ZLOCK_HPP
#include "memory/allocation.hpp"
-#include <pthread.h>
+#include "runtime/os.hpp"
class ZLock {
private:
- pthread_mutex_t _lock;
+ os::PlatformMutex _lock;
public:
- ZLock();
- ~ZLock();
-
void lock();
bool try_lock();
void unlock();
--- a/src/hotspot/share/gc/z/zLock.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zLock.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -26,27 +26,20 @@
#include "gc/z/zLock.hpp"
#include "runtime/atomic.hpp"
+#include "runtime/os.inline.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
-inline ZLock::ZLock() {
- pthread_mutex_init(&_lock, NULL);
-}
-
-inline ZLock::~ZLock() {
- pthread_mutex_destroy(&_lock);
-}
-
inline void ZLock::lock() {
- pthread_mutex_lock(&_lock);
+ _lock.lock();
}
inline bool ZLock::try_lock() {
- return pthread_mutex_trylock(&_lock) == 0;
+ return _lock.try_lock();
}
inline void ZLock::unlock() {
- pthread_mutex_unlock(&_lock);
+ _lock.unlock();
}
inline ZReentrantLock::ZReentrantLock() :
--- a/src/hotspot/share/gc/z/zStat.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zStat.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -755,7 +755,7 @@
//
// Stat timer
//
-__thread uint32_t ZStatTimerDisable::_active = 0;
+THREAD_LOCAL uint32_t ZStatTimerDisable::_active = 0;
//
// Stat sample/inc
--- a/src/hotspot/share/gc/z/zStat.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zStat.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -29,6 +29,7 @@
#include "gc/z/zMetronome.hpp"
#include "logging/logHandle.hpp"
#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
#include "utilities/numberSeq.hpp"
#include "utilities/ticks.hpp"
@@ -271,7 +272,7 @@
//
class ZStatTimerDisable : public StackObj {
private:
- static __thread uint32_t _active;
+ static THREAD_LOCAL uint32_t _active;
public:
ZStatTimerDisable() {
--- a/src/hotspot/share/gc/z/zThread.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zThread.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,13 +26,13 @@
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
-__thread bool ZThread::_initialized;
-__thread uintptr_t ZThread::_id;
-__thread bool ZThread::_is_vm;
-__thread bool ZThread::_is_java;
-__thread bool ZThread::_is_worker;
-__thread bool ZThread::_is_runtime_worker;
-__thread uint ZThread::_worker_id;
+THREAD_LOCAL bool ZThread::_initialized;
+THREAD_LOCAL uintptr_t ZThread::_id;
+THREAD_LOCAL bool ZThread::_is_vm;
+THREAD_LOCAL bool ZThread::_is_java;
+THREAD_LOCAL bool ZThread::_is_worker;
+THREAD_LOCAL bool ZThread::_is_runtime_worker;
+THREAD_LOCAL uint ZThread::_worker_id;
void ZThread::initialize() {
assert(!_initialized, "Already initialized");
--- a/src/hotspot/share/gc/z/zThread.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/gc/z/zThread.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,6 +25,7 @@
#define SHARE_GC_Z_ZTHREAD_HPP
#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
#include "utilities/debug.hpp"
class ZThread : public AllStatic {
@@ -33,13 +34,13 @@
friend class ZRuntimeWorkersInitializeTask;
private:
- static __thread bool _initialized;
- static __thread uintptr_t _id;
- static __thread bool _is_vm;
- static __thread bool _is_java;
- static __thread bool _is_worker;
- static __thread bool _is_runtime_worker;
- static __thread uint _worker_id;
+ static THREAD_LOCAL bool _initialized;
+ static THREAD_LOCAL uintptr_t _id;
+ static THREAD_LOCAL bool _is_vm;
+ static THREAD_LOCAL bool _is_java;
+ static THREAD_LOCAL bool _is_worker;
+ static THREAD_LOCAL bool _is_runtime_worker;
+ static THREAD_LOCAL uint _worker_id;
static void initialize();
--- a/src/hotspot/share/gc/z/zUtils.cpp Thu Sep 12 15:04:00 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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.
- *
- * 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.
- */
-
-#include "precompiled.hpp"
-#include "gc/z/zUtils.inline.hpp"
-#include "utilities/debug.hpp"
-
-#include <stdlib.h>
-
-uintptr_t ZUtils::alloc_aligned(size_t alignment, size_t size) {
- void* res = NULL;
-
- if (posix_memalign(&res, alignment, size) != 0) {
- fatal("posix_memalign() failed");
- }
-
- memset(res, 0, size);
-
- return (uintptr_t)res;
-}
--- a/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -2436,7 +2436,7 @@
handle_exception);
result = THREAD->vm_result();
}
- if (oopDesc::equals(result, Universe::the_null_sentinel()))
+ if (result == Universe::the_null_sentinel())
result = NULL;
VERIFY_OOP(result);
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -206,7 +206,7 @@
if (rindex >= 0) {
oop coop = m->constants()->resolved_references()->obj_at(rindex);
oop roop = (result == NULL ? Universe::the_null_sentinel() : result);
- assert(oopDesc::equals(roop, coop), "expected result for assembly code");
+ assert(roop == coop, "expected result for assembly code");
}
}
#endif
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -44,7 +44,6 @@
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/array.hpp"
-#include "oops/constantPool.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
@@ -1522,7 +1521,7 @@
assert(new_method != NULL, "invariant");
assert(new_method->name() == old_method->name(), "invariant");
assert(new_method->signature() == old_method->signature(), "invariant");
- *new_method->trace_flags_addr() = old_method->trace_flags();
+ new_method->set_trace_flags(old_method->trace_flags());
assert(new_method->trace_flags() == old_method->trace_flags(), "invariant");
}
}
--- a/src/hotspot/share/jfr/jfr.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/jfr.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -84,7 +84,9 @@
}
void Jfr::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
- LeakProfiler::oops_do(is_alive, f);
+ if (LeakProfiler::is_running()) {
+ LeakProfiler::oops_do(is_alive, f);
+ }
}
bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
--- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -55,18 +55,23 @@
return !_edges->has_entries();
}
-void EdgeStore::assign_id(EdgeEntry* entry) {
+void EdgeStore::on_link(EdgeEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_edge_id_counter);
}
-bool EdgeStore::equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry) {
+bool EdgeStore::on_equals(uintptr_t hash, const EdgeEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
return true;
}
+void EdgeStore::on_unlink(EdgeEntry* entry) {
+ assert(entry != NULL, "invariant");
+ // nothing
+}
+
#ifdef ASSERT
bool EdgeStore::contains(const oop* reference) const {
return get(reference) != NULL;
@@ -75,22 +80,21 @@
StoredEdge* EdgeStore::get(const oop* reference) const {
assert(reference != NULL, "invariant");
- const StoredEdge e(NULL, reference);
- EdgeEntry* const entry = _edges->lookup_only(e, (uintptr_t)reference);
+ EdgeEntry* const entry = _edges->lookup_only((uintptr_t)reference);
return entry != NULL ? entry->literal_addr() : NULL;
}
StoredEdge* EdgeStore::put(const oop* reference) {
assert(reference != NULL, "invariant");
const StoredEdge e(NULL, reference);
- assert(NULL == _edges->lookup_only(e, (uintptr_t)reference), "invariant");
- EdgeEntry& entry = _edges->put(e, (uintptr_t)reference);
+ assert(NULL == _edges->lookup_only((uintptr_t)reference), "invariant");
+ EdgeEntry& entry = _edges->put((uintptr_t)reference, e);
return entry.literal_addr();
}
traceid EdgeStore::get_id(const Edge* edge) const {
assert(edge != NULL, "invariant");
- EdgeEntry* const entry = _edges->lookup_only(*edge, (uintptr_t)edge->reference());
+ EdgeEntry* const entry = _edges->lookup_only((uintptr_t)edge->reference());
assert(entry != NULL, "invariant");
return entry->id();
}
--- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -58,7 +58,7 @@
};
class EdgeStore : public CHeapObj<mtTracing> {
- typedef HashTableHost<StoredEdge, traceid, Entry, EdgeStore> EdgeHashTable;
+ typedef HashTableHost<StoredEdge, traceid, JfrHashtableEntry, EdgeStore> EdgeHashTable;
typedef EdgeHashTable::HashEntry EdgeEntry;
template <typename,
typename,
@@ -74,8 +74,9 @@
EdgeHashTable* _edges;
// Hash table callbacks
- void assign_id(EdgeEntry* entry);
- bool equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry);
+ void on_link(EdgeEntry* entry);
+ bool on_equals(uintptr_t hash, const EdgeEntry* entry);
+ void on_unlink(EdgeEntry* entry);
StoredEdge* get(const oop* reference) const;
StoredEdge* put(const oop* reference);
--- a/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -43,7 +43,6 @@
#include "jfr/leakprofiler/utilities/granularTimer.hpp"
#include "logging/log.hpp"
#include "memory/universe.hpp"
-#include "oops/markWord.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/safepoint.hpp"
#include "utilities/globalDefinitions.hpp"
@@ -101,7 +100,7 @@
// Save the original markWord for the potential leak objects,
// to be restored on function exit
ObjectSampleMarker marker;
- if (ObjectSampleCheckpoint::mark(_sampler, marker, _emit_all) == 0) {
+ if (ObjectSampleCheckpoint::save_mark_words(_sampler, marker, _emit_all) == 0) {
// no valid samples to process
return;
}
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -24,10 +24,6 @@
#include "precompiled.hpp"
#include "jfr/jfrEvents.hpp"
-#include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
-#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/leakprofiler/chains/edgeStore.hpp"
#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
@@ -35,14 +31,101 @@
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
-#include "jfr/leakprofiler/utilities/rootType.hpp"
-#include "jfr/metadata/jfrSerializer.hpp"
-#include "runtime/interfaceSupport.inline.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/thread.inline.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
+#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
+#include "jfr/utilities/jfrHashtable.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+#include "runtime/safepoint.hpp"
+#include "runtime/thread.hpp"
+#include "utilities/growableArray.hpp"
+
+static bool predicate(GrowableArray<traceid>* set, traceid id) {
+ assert(set != NULL, "invariant");
+ bool found = false;
+ set->find_sorted<traceid, compare_traceid>(id, found);
+ return found;
+}
+
+static bool mutable_predicate(GrowableArray<traceid>* set, traceid id) {
+ assert(set != NULL, "invariant");
+ bool found = false;
+ const int location = set->find_sorted<traceid, compare_traceid>(id, found);
+ if (!found) {
+ set->insert_before(location, id);
+ }
+ return found;
+}
+
+static bool add(GrowableArray<traceid>* set, traceid id) {
+ assert(set != NULL, "invariant");
+ return mutable_predicate(set, id);
+}
+
+const int initial_array_size = 64;
+
+template <typename T>
+static GrowableArray<T>* c_heap_allocate_array(int size = initial_array_size) {
+ return new (ResourceObj::C_HEAP, mtTracing) GrowableArray<T>(size, true, mtTracing);
+}
+
+static GrowableArray<traceid>* unloaded_thread_id_set = NULL;
-template <typename SampleProcessor>
-static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) {
+class ThreadIdExclusiveAccess : public StackObj {
+ private:
+ static Semaphore _mutex_semaphore;
+ public:
+ ThreadIdExclusiveAccess() { _mutex_semaphore.wait(); }
+ ~ThreadIdExclusiveAccess() { _mutex_semaphore.signal(); }
+};
+
+Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1);
+
+static bool has_thread_exited(traceid tid) {
+ assert(tid != 0, "invariant");
+ return unloaded_thread_id_set != NULL && predicate(unloaded_thread_id_set, tid);
+}
+
+static void add_to_unloaded_thread_set(traceid tid) {
+ ThreadIdExclusiveAccess lock;
+ if (unloaded_thread_id_set == NULL) {
+ unloaded_thread_id_set = c_heap_allocate_array<traceid>();
+ }
+ add(unloaded_thread_id_set, tid);
+}
+
+void ObjectSampleCheckpoint::on_thread_exit(JavaThread* jt) {
+ assert(jt != NULL, "invariant");
+ if (LeakProfiler::is_running()) {
+ add_to_unloaded_thread_set(jt->jfr_thread_local()->thread_id());
+ }
+}
+
+// Track the set of unloaded klasses during a chunk / epoch.
+// Methods in stacktraces belonging to unloaded klasses must not be accessed.
+static GrowableArray<traceid>* unloaded_klass_set = NULL;
+
+static void add_to_unloaded_klass_set(traceid klass_id) {
+ if (unloaded_klass_set == NULL) {
+ unloaded_klass_set = c_heap_allocate_array<traceid>();
+ }
+ unloaded_klass_set->append(klass_id);
+}
+
+static void sort_unloaded_klass_set() {
+ if (unloaded_klass_set != NULL && unloaded_klass_set->length() > 1) {
+ unloaded_klass_set->sort(sort_traceid);
+ }
+}
+
+void ObjectSampleCheckpoint::on_klass_unload(const Klass* k) {
+ assert(k != NULL, "invariant");
+ add_to_unloaded_klass_set(TRACE_ID(k));
+}
+
+template <typename Processor>
+static void do_samples(ObjectSample* sample, const ObjectSample* end, Processor& processor) {
assert(sample != NULL, "invariant");
while (sample != end) {
processor.sample_do(sample);
@@ -50,244 +133,339 @@
}
}
-class RootSystemType : public JfrSerializer {
- public:
- void serialize(JfrCheckpointWriter& writer) {
- const u4 nof_root_systems = OldObjectRoot::_number_of_systems;
- writer.write_count(nof_root_systems);
- for (u4 i = 0; i < nof_root_systems; ++i) {
- writer.write_key(i);
- writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i));
- }
- }
-};
+template <typename Processor>
+static void iterate_samples(Processor& processor, bool all = false) {
+ ObjectSampler* const sampler = ObjectSampler::sampler();
+ assert(sampler != NULL, "invariant");
+ ObjectSample* const last = sampler->last();
+ assert(last != NULL, "invariant");
+ do_samples(last, all ? NULL : sampler->last_resolved(), processor);
+}
-class RootType : public JfrSerializer {
+class SampleMarker {
+ private:
+ ObjectSampleMarker& _marker;
+ jlong _last_sweep;
+ int _count;
public:
- void serialize(JfrCheckpointWriter& writer) {
- const u4 nof_root_types = OldObjectRoot::_number_of_types;
- writer.write_count(nof_root_types);
- for (u4 i = 0; i < nof_root_types; ++i) {
- writer.write_key(i);
- writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i));
- }
- }
-};
-
-class CheckpointInstall {
- private:
- const JfrCheckpointBlobHandle& _cp;
- public:
- CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {}
+ SampleMarker(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), _last_sweep(last_sweep), _count(0) {}
void sample_do(ObjectSample* sample) {
- assert(sample != NULL, "invariant");
- if (!sample->is_dead()) {
- sample->set_klass_checkpoint(_cp);
+ if (sample->is_alive_and_older_than(_last_sweep)) {
+ _marker.mark(sample->object());
+ ++_count;
}
}
-};
-
-class CheckpointWrite {
- private:
- JfrCheckpointWriter& _writer;
- const jlong _last_sweep;
- public:
- CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {}
- void sample_do(ObjectSample* sample) {
- assert(sample != NULL, "invariant");
- if (sample->is_alive_and_older_than(_last_sweep)) {
- if (sample->has_thread_checkpoint()) {
- const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
- thread_cp->exclusive_write(_writer);
- }
- if (sample->has_klass_checkpoint()) {
- const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
- klass_cp->exclusive_write(_writer);
- }
- }
- }
-};
-
-class CheckpointStateReset {
- private:
- const jlong _last_sweep;
- public:
- CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {}
- void sample_do(ObjectSample* sample) {
- assert(sample != NULL, "invariant");
- if (sample->is_alive_and_older_than(_last_sweep)) {
- if (sample->has_thread_checkpoint()) {
- const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
- thread_cp->reset_write_state();
- }
- if (sample->has_klass_checkpoint()) {
- const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
- klass_cp->reset_write_state();
- }
- }
- }
-};
-
-class StackTraceWrite {
- private:
- JfrStackTraceRepository& _stack_trace_repo;
- JfrCheckpointWriter& _writer;
- int _count;
- public:
- StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) :
- _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) {
- JfrStacktrace_lock->lock_without_safepoint_check();
- }
- ~StackTraceWrite() {
- assert(JfrStacktrace_lock->owned_by_self(), "invariant");
- JfrStacktrace_lock->unlock();
- }
-
- void sample_do(ObjectSample* sample) {
- assert(sample != NULL, "invariant");
- if (!sample->is_dead()) {
- if (sample->has_stack_trace()) {
- JfrTraceId::use(sample->klass(), true);
- _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash());
- ++_count;
- }
- }
- }
-
int count() const {
return _count;
}
};
-class SampleMark {
+int ObjectSampleCheckpoint::save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all) {
+ assert(sampler != NULL, "invariant");
+ if (sampler->last() == NULL) {
+ return 0;
+ }
+ SampleMarker sample_marker(marker, emit_all ? max_jlong : sampler->last_sweep().value());
+ iterate_samples(sample_marker, true);
+ return sample_marker.count();
+}
+
+class BlobCache {
+ typedef HashTableHost<JfrBlobHandle, traceid, JfrHashtableEntry, BlobCache> BlobTable;
+ typedef BlobTable::HashEntry BlobEntry;
private:
- ObjectSampleMarker& _marker;
- jlong _last_sweep;
- int _count;
+ BlobTable _table;
+ traceid _lookup_id;
public:
- SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker),
- _last_sweep(last_sweep),
- _count(0) {}
+ BlobCache(size_t size) : _table(this, size), _lookup_id(0) {}
+ JfrBlobHandle get(const ObjectSample* sample);
+ void put(const ObjectSample* sample, const JfrBlobHandle& blob);
+ // Hash table callbacks
+ void on_link(const BlobEntry* entry) const;
+ bool on_equals(uintptr_t hash, const BlobEntry* entry) const;
+ void on_unlink(BlobEntry* entry) const;
+};
+
+JfrBlobHandle BlobCache::get(const ObjectSample* sample) {
+ assert(sample != NULL, "invariant");
+ _lookup_id = sample->stack_trace_id();
+ assert(_lookup_id != 0, "invariant");
+ BlobEntry* const entry = _table.lookup_only(sample->stack_trace_hash());
+ return entry != NULL ? entry->literal() : JfrBlobHandle();
+}
+
+void BlobCache::put(const ObjectSample* sample, const JfrBlobHandle& blob) {
+ assert(sample != NULL, "invariant");
+ assert(_table.lookup_only(sample->stack_trace_hash()) == NULL, "invariant");
+ _lookup_id = sample->stack_trace_id();
+ assert(_lookup_id != 0, "invariant");
+ _table.put(sample->stack_trace_hash(), blob);
+}
+
+inline void BlobCache::on_link(const BlobEntry* entry) const {
+ assert(entry != NULL, "invariant");
+ assert(entry->id() == 0, "invariant");
+ entry->set_id(_lookup_id);
+}
+
+inline bool BlobCache::on_equals(uintptr_t hash, const BlobEntry* entry) const {
+ assert(entry != NULL, "invariant");
+ assert(entry->hash() == hash, "invariant");
+ return entry->id() == _lookup_id;
+}
+
+inline void BlobCache::on_unlink(BlobEntry* entry) const {
+ assert(entry != NULL, "invariant");
+}
+
+static GrowableArray<traceid>* id_set = NULL;
+
+static void prepare_for_resolution() {
+ id_set = new GrowableArray<traceid>(JfrOptionSet::old_object_queue_size());
+ sort_unloaded_klass_set();
+}
+
+static bool stack_trace_precondition(const ObjectSample* sample) {
+ assert(sample != NULL, "invariant");
+ return sample->has_stack_trace_id() && !sample->is_dead();
+}
+
+class StackTraceBlobInstaller {
+ private:
+ const JfrStackTraceRepository& _stack_trace_repo;
+ BlobCache _cache;
+ const JfrStackTrace* resolve(const ObjectSample* sample);
+ void install(ObjectSample* sample);
+ public:
+ StackTraceBlobInstaller(const JfrStackTraceRepository& stack_trace_repo);
void sample_do(ObjectSample* sample) {
- assert(sample != NULL, "invariant");
- if (sample->is_alive_and_older_than(_last_sweep)) {
- _marker.mark(sample->object());
- ++_count;
+ if (stack_trace_precondition(sample)) {
+ install(sample);
}
}
-
- int count() const {
- return _count;
- }
};
-void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool type_set) {
- if (!writer.has_data()) {
+StackTraceBlobInstaller::StackTraceBlobInstaller(const JfrStackTraceRepository& stack_trace_repo) :
+ _stack_trace_repo(stack_trace_repo), _cache(JfrOptionSet::old_object_queue_size()) {
+ prepare_for_resolution();
+}
+
+const JfrStackTrace* StackTraceBlobInstaller::resolve(const ObjectSample* sample) {
+ return _stack_trace_repo.lookup(sample->stack_trace_hash(), sample->stack_trace_id());
+}
+
+#ifdef ASSERT
+static void validate_stack_trace(const ObjectSample* sample, const JfrStackTrace* stack_trace) {
+ assert(!sample->has_stacktrace(), "invariant");
+ assert(stack_trace != NULL, "invariant");
+ assert(stack_trace->hash() == sample->stack_trace_hash(), "invariant");
+ assert(stack_trace->id() == sample->stack_trace_id(), "invariant");
+}
+#endif
+
+void StackTraceBlobInstaller::install(ObjectSample* sample) {
+ JfrBlobHandle blob = _cache.get(sample);
+ if (blob.valid()) {
+ sample->set_stacktrace(blob);
return;
}
-
- assert(writer.has_data(), "invariant");
- const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob();
- CheckpointInstall install(h_cp);
-
- // Class unload implies a safepoint.
- // Not class unload implies the object sampler is locked, because it was claimed exclusively earlier.
- // Therefore: direct access the object sampler instance is safe.
- ObjectSampler* const object_sampler = ObjectSampler::sampler();
- assert(object_sampler != NULL, "invariant");
+ const JfrStackTrace* const stack_trace = resolve(sample);
+ DEBUG_ONLY(validate_stack_trace(sample, stack_trace));
+ JfrCheckpointWriter writer(false, true, Thread::current());
+ writer.write_type(TYPE_STACKTRACE);
+ writer.write_count(1);
+ ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);
+ blob = writer.move();
+ _cache.put(sample, blob);
+ sample->set_stacktrace(blob);
+}
- ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
- const ObjectSample* const last_resolved = object_sampler->last_resolved();
-
- // install only to new samples since last resolved checkpoint
- if (last != last_resolved) {
- do_samples(last, last_resolved, install);
- if (class_unload) {
- return;
- }
- if (type_set) {
- object_sampler->set_last_resolved(last);
- }
+static void install_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) {
+ assert(sampler != NULL, "invariant");
+ const ObjectSample* const last = sampler->last();
+ if (last != sampler->last_resolved()) {
+ StackTraceBlobInstaller installer(stack_trace_repo);
+ iterate_samples(installer);
}
}
-void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
+// caller needs ResourceMark
+void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) {
+ assert(sampler != NULL, "invariant");
+ assert(LeakProfiler::is_running(), "invariant");
+ install_stack_traces(sampler, stack_trace_repo);
+}
+
+static traceid get_klass_id(traceid method_id) {
+ assert(method_id != 0, "invariant");
+ return method_id >> TRACE_ID_SHIFT;
+}
+
+static bool is_klass_unloaded(traceid method_id) {
+ return unloaded_klass_set != NULL && predicate(unloaded_klass_set, get_klass_id(method_id));
+}
+
+static bool is_processed(traceid id) {
+ assert(id != 0, "invariant");
+ assert(id_set != NULL, "invariant");
+ return mutable_predicate(id_set, id);
+}
+
+void ObjectSampleCheckpoint::add_to_leakp_set(const Method* method, traceid method_id) {
+ if (is_processed(method_id) || is_klass_unloaded(method_id)) {
+ return;
+ }
+ JfrTraceId::set_leakp(method);
+}
+
+void ObjectSampleCheckpoint::write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer) {
+ assert(trace != NULL, "invariant");
+ // JfrStackTrace
+ writer.write(trace->id());
+ writer.write((u1)!trace->_reached_root);
+ writer.write(trace->_nr_of_frames);
+ // JfrStackFrames
+ for (u4 i = 0; i < trace->_nr_of_frames; ++i) {
+ const JfrStackFrame& frame = trace->_frames[i];
+ frame.write(writer);
+ add_to_leakp_set(frame._method, frame._methodid);
+ }
+}
+
+static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer, bool reset) {
+ if (reset) {
+ blob->reset_write_state();
+ return;
+ }
+ blob->exclusive_write(writer);
+}
+
+static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {
+ if (sample->has_type_set()) {
+ write_blob(sample->type_set(), writer, reset);
+ }
+}
+
+static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {
+ assert(sample->has_thread(), "invariant");
+ if (has_thread_exited(sample->thread_id())) {
+ write_blob(sample->thread(), writer, reset);
+ }
+}
+
+static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {
+ if (sample->has_stacktrace()) {
+ write_blob(sample->stacktrace(), writer, reset);
+ }
+}
+
+static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {
+ assert(sample != NULL, "invariant");
+ write_stacktrace_blob(sample, writer, reset);
+ write_thread_blob(sample, writer, reset);
+ write_type_set_blob(sample, writer, reset);
+}
+
+class BlobWriter {
+ private:
+ const ObjectSampler* _sampler;
+ JfrCheckpointWriter& _writer;
+ const jlong _last_sweep;
+ bool _reset;
+ public:
+ BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) :
+ _sampler(sampler), _writer(writer), _last_sweep(last_sweep), _reset(false) {}
+ void sample_do(ObjectSample* sample) {
+ if (sample->is_alive_and_older_than(_last_sweep)) {
+ write_blobs(sample, _writer, _reset);
+ }
+ }
+ void set_reset() {
+ _reset = true;
+ }
+};
+
+static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
+ // sample set is predicated on time of last sweep
+ const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
+ JfrCheckpointWriter writer(false, false, thread);
+ BlobWriter cbw(sampler, writer, last_sweep);
+ iterate_samples(cbw, true);
+ // reset blob write states
+ cbw.set_reset();
+ iterate_samples(cbw, true);
+}
+
+void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
assert(sampler != NULL, "invariant");
assert(edge_store != NULL, "invariant");
assert(thread != NULL, "invariant");
-
- static bool types_registered = false;
- if (!types_registered) {
- JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
- JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
- types_registered = true;
- }
-
- const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
- ObjectSample* const last = const_cast<ObjectSample*>(sampler->last());
- {
- JfrCheckpointWriter writer(false, false, thread);
- CheckpointWrite checkpoint_write(writer, last_sweep);
- do_samples(last, NULL, checkpoint_write);
- }
-
- CheckpointStateReset state_reset(last_sweep);
- do_samples(last, NULL, state_reset);
-
+ write_sample_blobs(sampler, emit_all, thread);
+ // write reference chains
if (!edge_store->is_empty()) {
- // java object and chain representations
JfrCheckpointWriter writer(false, true, thread);
ObjectSampleWriter osw(writer, edge_store);
edge_store->iterate(osw);
}
}
-int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) {
- assert(object_sampler != NULL, "invariant");
- ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
- if (last == NULL) {
- return 0;
+static void clear_unloaded_klass_set() {
+ if (unloaded_klass_set != NULL && unloaded_klass_set->is_nonempty()) {
+ unloaded_klass_set->clear();
}
- const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
- SampleMark mark(marker, last_sweep);
- do_samples(last, NULL, mark);
- return mark.count();
+}
+
+// A linked list of saved type set blobs for the epoch.
+// The link consist of a reference counted handle.
+static JfrBlobHandle saved_type_set_blobs;
+
+static void release_state_for_previous_epoch() {
+ // decrements the reference count and the list is reinitialized
+ saved_type_set_blobs = JfrBlobHandle();
+ clear_unloaded_klass_set();
}
-WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) :
- _sampler(sampler), _stack_trace_repo(repo) {}
-
-bool WriteObjectSampleStacktrace::process() {
- assert(LeakProfiler::is_running(), "invariant");
- assert(_sampler != NULL, "invariant");
+class BlobInstaller {
+ public:
+ ~BlobInstaller() {
+ release_state_for_previous_epoch();
+ }
+ void sample_do(ObjectSample* sample) {
+ if (!sample->is_dead()) {
+ sample->set_type_set(saved_type_set_blobs);
+ }
+ }
+};
- ObjectSample* const last = const_cast<ObjectSample*>(_sampler->last());
- const ObjectSample* const last_resolved = _sampler->last_resolved();
- if (last == last_resolved) {
- return true;
- }
-
- JfrCheckpointWriter writer(false, true, Thread::current());
- const JfrCheckpointContext ctx = writer.context();
-
- writer.write_type(TYPE_STACKTRACE);
- const jlong count_offset = writer.reserve(sizeof(u4));
+static void install_type_set_blobs() {
+ BlobInstaller installer;
+ iterate_samples(installer);
+}
- int count = 0;
- {
- StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock
- do_samples(last, last_resolved, stack_trace_write);
- count = stack_trace_write.count();
+static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) {
+ assert(writer.has_data(), "invariant");
+ const JfrBlobHandle blob = copy ? writer.copy() : writer.move();
+ if (saved_type_set_blobs.valid()) {
+ saved_type_set_blobs->set_next(blob);
+ } else {
+ saved_type_set_blobs = blob;
}
- if (count == 0) {
- writer.set_context(ctx);
- return true;
+}
+
+void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) {
+ assert(LeakProfiler::is_running(), "invariant");
+ const ObjectSample* last = ObjectSampler::sampler()->last();
+ if (writer.has_data() && last != NULL) {
+ save_type_set_blob(writer);
+ install_type_set_blobs();
+ ObjectSampler::sampler()->set_last_resolved(last);
}
- assert(count > 0, "invariant");
- writer.write_count((u4)count, count_offset);
- JfrStackTraceRepository::write_metadata(writer);
+}
- // install the stacktrace checkpoint information to the candidates
- ObjectSampleCheckpoint::install(writer, false, false);
- return true;
+void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ assert(LeakProfiler::is_running(), "invariant");
+ if (writer.has_data() && ObjectSampler::sampler()->last() != NULL) {
+ save_type_set_blob(writer, true);
+ }
}
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,27 +26,35 @@
#define SHARE_JFR_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
#include "memory/allocation.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
class EdgeStore;
+class JavaThread;
class JfrCheckpointWriter;
+class JfrStackTrace;
class JfrStackTraceRepository;
+class Klass;
+class Method;
+class ObjectSample;
class ObjectSampleMarker;
class ObjectSampler;
+class Thread;
class ObjectSampleCheckpoint : AllStatic {
+ friend class EventEmitter;
+ friend class PathToGcRootsOperation;
+ friend class StackTraceBlobInstaller;
+ private:
+ static void add_to_leakp_set(const Method* method, traceid method_id);
+ static int save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all);
+ static void write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer);
+ static void write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread);
public:
- static void install(JfrCheckpointWriter& writer, bool class_unload, bool type_set);
- static void write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread);
- static int mark(ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all);
-};
-
-class WriteObjectSampleStacktrace : public StackObj {
- private:
- ObjectSampler* const _sampler;
- JfrStackTraceRepository& _stack_trace_repo;
- public:
- WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo);
- bool process();
+ static void on_klass_unload(const Klass* k);
+ static void on_type_set(JfrCheckpointWriter& writer);
+ static void on_type_set_unload(JfrCheckpointWriter& writer);
+ static void on_thread_exit(JavaThread* jt);
+ static void on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& repo);
};
#endif // SHARE_JFR_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -33,8 +33,8 @@
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "jfr/leakprofiler/utilities/rootType.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
-#include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp"
-#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp"
+#include "jfr/metadata/jfrSerializer.hpp"
+#include "jfr/writers/jfrTypeWriterHost.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "utilities/growableArray.hpp"
@@ -137,30 +137,33 @@
typename,
size_t>
friend class HashTableHost;
- typedef HashTableHost<const ObjectSampleFieldInfo*, traceid, Entry, FieldTable, 109> FieldInfoTable;
+ typedef HashTableHost<const ObjectSampleFieldInfo*, traceid, JfrHashtableEntry, FieldTable, 109> FieldInfoTable;
public:
typedef FieldInfoTable::HashEntry FieldInfoEntry;
private:
static traceid _field_id_counter;
FieldInfoTable* _table;
+ const ObjectSampleFieldInfo* _lookup;
- void assign_id(FieldInfoEntry* entry) {
+ void on_link(FieldInfoEntry* entry) {
assert(entry != NULL, "invariant");
entry->set_id(++_field_id_counter);
}
- bool equals(const ObjectSampleFieldInfo* query, uintptr_t hash, const FieldInfoEntry* entry) {
+ bool on_equals(uintptr_t hash, const FieldInfoEntry* entry) {
assert(hash == entry->hash(), "invariant");
- assert(query != NULL, "invariant");
- const ObjectSampleFieldInfo* stored = entry->literal();
- assert(stored != NULL, "invariant");
- assert(stored->_field_name_symbol->identity_hash() == query->_field_name_symbol->identity_hash(), "invariant");
- return stored->_field_modifiers == query->_field_modifiers;
+ assert(_lookup != NULL, "invariant");
+ return entry->literal()->_field_modifiers == _lookup->_field_modifiers;
+ }
+
+ void on_unlink(FieldInfoEntry* entry) {
+ assert(entry != NULL, "invariant");
+ // nothing
}
public:
- FieldTable() : _table(new FieldInfoTable(this)) {}
+ FieldTable() : _table(new FieldInfoTable(this)), _lookup(NULL) {}
~FieldTable() {
assert(_table != NULL, "invariant");
delete _table;
@@ -168,8 +171,8 @@
traceid store(const ObjectSampleFieldInfo* field_info) {
assert(field_info != NULL, "invariant");
- const FieldInfoEntry& entry =_table->lookup_put(field_info,
- field_info->_field_name_symbol->identity_hash());
+ _lookup = field_info;
+ const FieldInfoEntry& entry = _table->lookup_put(field_info->_field_name_symbol->identity_hash(), field_info);
return entry.id();
}
@@ -196,7 +199,7 @@
static FieldTable* field_infos = NULL;
static RootDescriptionInfo* root_infos = NULL;
-int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* si) {
+int __write_sample_info__(JfrCheckpointWriter* writer, const void* si) {
assert(writer != NULL, "invariant");
assert(si != NULL, "invariant");
const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si;
@@ -211,17 +214,17 @@
return 1;
}
-typedef JfrArtifactWriterImplHost<const OldObjectSampleInfo*, __write_sample_info__> SampleWriterImpl;
-typedef JfrArtifactWriterHost<SampleWriterImpl, TYPE_OLDOBJECT> SampleWriter;
+typedef JfrTypeWriterImplHost<const OldObjectSampleInfo*, __write_sample_info__> SampleWriterImpl;
+typedef JfrTypeWriterHost<SampleWriterImpl, TYPE_OLDOBJECT> SampleWriter;
static void write_sample_infos(JfrCheckpointWriter& writer) {
if (sample_infos != NULL) {
- SampleWriter sw(&writer, NULL, false);
+ SampleWriter sw(&writer);
sample_infos->iterate(sw);
}
}
-int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) {
+int __write_reference_info__(JfrCheckpointWriter* writer, const void* ri) {
assert(writer != NULL, "invariant");
assert(ri != NULL, "invariant");
const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri;
@@ -233,17 +236,17 @@
return 1;
}
-typedef JfrArtifactWriterImplHost<const ReferenceInfo*, __write_reference_info__> ReferenceWriterImpl;
-typedef JfrArtifactWriterHost<ReferenceWriterImpl, TYPE_REFERENCE> ReferenceWriter;
+typedef JfrTypeWriterImplHost<const ReferenceInfo*, __write_reference_info__> ReferenceWriterImpl;
+typedef JfrTypeWriterHost<ReferenceWriterImpl, TYPE_REFERENCE> ReferenceWriter;
static void write_reference_infos(JfrCheckpointWriter& writer) {
if (ref_infos != NULL) {
- ReferenceWriter rw(&writer, NULL, false);
+ ReferenceWriter rw(&writer);
ref_infos->iterate(rw);
}
}
-int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) {
+int __write_array_info__(JfrCheckpointWriter* writer, const void* ai) {
assert(writer != NULL, "invariant");
assert(ai != NULL, "invariant");
const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai;
@@ -270,17 +273,17 @@
return array_infos->store(osai);
}
-typedef JfrArtifactWriterImplHost<const ObjectSampleArrayInfo*, __write_array_info__> ArrayWriterImpl;
-typedef JfrArtifactWriterHost<ArrayWriterImpl, TYPE_OLDOBJECTARRAY> ArrayWriter;
+typedef JfrTypeWriterImplHost<const ObjectSampleArrayInfo*, __write_array_info__> ArrayWriterImpl;
+typedef JfrTypeWriterHost<ArrayWriterImpl, TYPE_OLDOBJECTARRAY> ArrayWriter;
static void write_array_infos(JfrCheckpointWriter& writer) {
if (array_infos != NULL) {
- ArrayWriter aw(&writer, NULL, false);
+ ArrayWriter aw(&writer);
array_infos->iterate(aw);
}
}
-int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) {
+int __write_field_info__(JfrCheckpointWriter* writer, const void* fi) {
assert(writer != NULL, "invariant");
assert(fi != NULL, "invariant");
const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi;
@@ -314,12 +317,12 @@
return field_infos->store(osfi);
}
-typedef JfrArtifactWriterImplHost<const FieldTable::FieldInfoEntry*, __write_field_info__> FieldWriterImpl;
-typedef JfrArtifactWriterHost<FieldWriterImpl, TYPE_OLDOBJECTFIELD> FieldWriter;
+typedef JfrTypeWriterImplHost<const FieldTable::FieldInfoEntry*, __write_field_info__> FieldWriterImpl;
+typedef JfrTypeWriterHost<FieldWriterImpl, TYPE_OLDOBJECTFIELD> FieldWriter;
static void write_field_infos(JfrCheckpointWriter& writer) {
if (field_infos != NULL) {
- FieldWriter fw(&writer, NULL, false);
+ FieldWriter fw(&writer);
field_infos->iterate(fw);
}
}
@@ -339,7 +342,7 @@
return description.description();
}
-int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) {
+int __write_root_description_info__(JfrCheckpointWriter* writer, const void* di) {
assert(writer != NULL, "invariant");
assert(di != NULL, "invariant");
const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di;
@@ -366,8 +369,8 @@
return root_infos->store(oodi);
}
-typedef JfrArtifactWriterImplHost<const ObjectSampleRootDescriptionInfo*, __write_root_description_info__> RootDescriptionWriterImpl;
-typedef JfrArtifactWriterHost<RootDescriptionWriterImpl, TYPE_OLDOBJECTGCROOT> RootDescriptionWriter;
+typedef JfrTypeWriterImplHost<const ObjectSampleRootDescriptionInfo*, __write_root_description_info__> RootDescriptionWriterImpl;
+typedef JfrTypeWriterHost<RootDescriptionWriterImpl, TYPE_OLDOBJECTGCROOT> RootDescriptionWriter;
int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) {
@@ -513,7 +516,7 @@
RootResolutionSet rrs(root_infos);
RootResolver::resolve(rrs);
// write roots
- RootDescriptionWriter rw(&writer, NULL, false);
+ RootDescriptionWriter rw(&writer);
root_infos->iterate(rw);
}
}
@@ -576,11 +579,45 @@
}
}
+class RootSystemType : public JfrSerializer {
+ public:
+ void serialize(JfrCheckpointWriter& writer) {
+ const u4 nof_root_systems = OldObjectRoot::_number_of_systems;
+ writer.write_count(nof_root_systems);
+ for (u4 i = 0; i < nof_root_systems; ++i) {
+ writer.write_key(i);
+ writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i));
+ }
+ }
+};
+
+class RootType : public JfrSerializer {
+ public:
+ void serialize(JfrCheckpointWriter& writer) {
+ const u4 nof_root_types = OldObjectRoot::_number_of_types;
+ writer.write_count(nof_root_types);
+ for (u4 i = 0; i < nof_root_types; ++i) {
+ writer.write_key(i);
+ writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i));
+ }
+ }
+};
+
+static void register_serializers() {
+ static bool is_registered = false;
+ if (!is_registered) {
+ JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
+ JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
+ is_registered = true;
+ }
+}
+
ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, EdgeStore* store) :
_writer(writer),
_store(store) {
assert(store != NULL, "invariant");
assert(!store->is_empty(), "invariant");
+ register_serializers();
sample_infos = NULL;
ref_infos = NULL;
array_infos = NULL;
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -32,7 +32,6 @@
#include "memory/iterator.hpp"
#include "memory/universe.hpp"
#include "oops/klass.hpp"
-#include "oops/markWord.hpp"
#include "oops/oop.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/frame.inline.hpp"
--- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,13 +25,14 @@
#ifndef SHARE_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP
#define SHARE_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP
-#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp"
#include "jfr/utilities/jfrAllocation.hpp"
+#include "jfr/utilities/jfrBlob.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
#include "utilities/ticks.hpp"
+
/*
* Handle for diagnosing Java memory leaks.
*
@@ -44,8 +45,9 @@
private:
ObjectSample* _next;
ObjectSample* _previous;
- JfrCheckpointBlobHandle _thread_cp;
- JfrCheckpointBlobHandle _klass_cp;
+ JfrBlobHandle _stacktrace;
+ JfrBlobHandle _thread;
+ JfrBlobHandle _type_set;
oop _object;
Ticks _allocation_time;
traceid _stack_trace_id;
@@ -62,17 +64,14 @@
}
void release_references() {
- if (_thread_cp.valid()) {
- _thread_cp.~JfrCheckpointBlobHandle();
- }
- if (_klass_cp.valid()) {
- _klass_cp.~JfrCheckpointBlobHandle();
- }
+ _stacktrace.~JfrBlobHandle();
+ _thread.~JfrBlobHandle();
+ _type_set.~JfrBlobHandle();
}
void reset() {
set_stack_trace_id(0);
- set_stack_trace_hash(0),
+ set_stack_trace_hash(0);
release_references();
_dead = false;
}
@@ -80,8 +79,9 @@
public:
ObjectSample() : _next(NULL),
_previous(NULL),
- _thread_cp(),
- _klass_cp(),
+ _stacktrace(),
+ _thread(),
+ _type_set(),
_object(NULL),
_allocation_time(),
_stack_trace_id(0),
@@ -174,7 +174,7 @@
return _heap_used_at_last_gc;
}
- bool has_stack_trace() const {
+ bool has_stack_trace_id() const {
return stack_trace_id() != 0;
}
@@ -194,10 +194,6 @@
_stack_trace_hash = hash;
}
- bool has_thread() const {
- return _thread_id != 0;
- }
-
traceid thread_id() const {
return _thread_id;
}
@@ -211,37 +207,51 @@
_allocation_time.ft_value() : _allocation_time.value()) < time_stamp;
}
- const JfrCheckpointBlobHandle& thread_checkpoint() const {
- return _thread_cp;
+ const JfrBlobHandle& stacktrace() const {
+ return _stacktrace;
}
- bool has_thread_checkpoint() const {
- return _thread_cp.valid();
+ bool has_stacktrace() const {
+ return _stacktrace.valid();
}
- // JfrCheckpointBlobHandle assignment operator
+ // JfrBlobHandle assignment operator
// maintains proper reference counting
- void set_thread_checkpoint(const JfrCheckpointBlobHandle& ref) {
- if (_thread_cp != ref) {
- _thread_cp = ref;
+ void set_stacktrace(const JfrBlobHandle& ref) {
+ if (_stacktrace != ref) {
+ _stacktrace = ref;
}
}
- const JfrCheckpointBlobHandle& klass_checkpoint() const {
- return _klass_cp;
+ const JfrBlobHandle& thread() const {
+ return _thread;
}
- bool has_klass_checkpoint() const {
- return _klass_cp.valid();
+ bool has_thread() const {
+ return _thread.valid();
+ }
+
+ void set_thread(const JfrBlobHandle& ref) {
+ if (_thread != ref) {
+ _thread = ref;
+ }
}
- void set_klass_checkpoint(const JfrCheckpointBlobHandle& ref) {
- if (_klass_cp != ref) {
- if (_klass_cp.valid()) {
- _klass_cp->set_next(ref);
+ const JfrBlobHandle& type_set() const {
+ return _type_set;
+ }
+
+ bool has_type_set() const {
+ return _type_set.valid();
+ }
+
+ void set_type_set(const JfrBlobHandle& ref) {
+ if (_type_set != ref) {
+ if (_type_set.valid()) {
+ _type_set->set_next(ref);
return;
}
- _klass_cp = ref;
+ _type_set = ref;
}
}
};
--- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -110,63 +110,42 @@
}
const JfrThreadLocal* const tl = thread->jfr_thread_local();
assert(tl != NULL, "invariant");
- if (!tl->has_thread_checkpoint()) {
- JfrCheckpointManager::create_thread_checkpoint(thread);
+ if (!tl->has_thread_blob()) {
+ JfrCheckpointManager::create_thread_blob(thread);
}
- assert(tl->has_thread_checkpoint(), "invariant");
+ assert(tl->has_thread_blob(), "invariant");
return tl->thread_id();
}
-// Populates the thread local stack frames, but does not add them
-// to the stacktrace repository (...yet, see stacktrace_id() below)
-//
-void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) {
- assert(stacktrace != NULL, "invariant");
+static void record_stacktrace(JavaThread* thread) {
assert(thread != NULL, "invariant");
if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) {
- JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0);
+ JfrStackTraceRepository::record_and_cache(thread);
}
}
-// We were successful in acquiring the try lock and have been selected for adding a sample.
-// Go ahead with installing our previously taken stacktrace into the stacktrace repository.
-//
-traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) {
- assert(stacktrace != NULL, "invariant");
- assert(stacktrace->hash() != 0, "invariant");
- const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread);
- thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash());
- return stacktrace_id;
-}
-
void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) {
assert(thread != NULL, "invariant");
assert(is_created(), "invariant");
-
const traceid thread_id = get_thread_id(thread);
if (thread_id == 0) {
return;
}
-
- const JfrThreadLocal* const tl = thread->jfr_thread_local();
- JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth());
- fill_stacktrace(&stacktrace, thread);
-
+ record_stacktrace(thread);
// try enter critical section
JfrTryLock tryLock(&_lock);
if (!tryLock.has_lock()) {
log_trace(jfr, oldobject, sampling)("Skipping old object sample due to lock contention");
return;
}
-
- instance().add(obj, allocated, thread_id, &stacktrace, thread);
+ instance().add(obj, allocated, thread_id, thread);
}
-void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) {
- assert(stacktrace != NULL, "invariant");
+void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JavaThread* thread) {
+ assert(obj != NULL, "invariant");
assert(thread_id != 0, "invariant");
assert(thread != NULL, "invariant");
- assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant");
+ assert(thread->jfr_thread_local()->has_thread_blob(), "invariant");
if (_dead_samples) {
scavenge();
@@ -190,11 +169,13 @@
assert(sample != NULL, "invariant");
sample->set_thread_id(thread_id);
- sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint());
+
+ const JfrThreadLocal* const tl = thread->jfr_thread_local();
+ sample->set_thread(tl->thread_blob());
- const unsigned int stacktrace_hash = stacktrace->hash();
+ const unsigned int stacktrace_hash = tl->cached_stack_trace_hash();
if (stacktrace_hash != 0) {
- sample->set_stack_trace_id(stacktrace_id(stacktrace, thread));
+ sample->set_stack_trace_id(tl->cached_stack_trace_id());
sample->set_stack_trace_hash(stacktrace_hash);
}
@@ -253,7 +234,7 @@
sampler._last_sweep = JfrTicks::now();
}
-const ObjectSample* ObjectSampler::last() const {
+ObjectSample* ObjectSampler::last() const {
return _list->last();
}
--- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -31,25 +31,19 @@
typedef u8 traceid;
class BoolObjectClosure;
-class JfrStackTrace;
+class JavaThread;
class OopClosure;
class ObjectSample;
-class ObjectSampler;
class SampleList;
class SamplePriorityQueue;
-class Thread;
// Class reponsible for holding samples and
// making sure the samples are evenly distributed as
// new entries are added and removed.
class ObjectSampler : public CHeapObj<mtTracing> {
- friend class EventEmitter;
- friend class JfrRecorderService;
friend class LeakProfiler;
friend class StartOperation;
friend class StopOperation;
- friend class ObjectSampleCheckpoint;
- friend class WriteObjectSampleStacktrace;
private:
SamplePriorityQueue* _priority_queue;
SampleList* _list;
@@ -64,20 +58,11 @@
~ObjectSampler();
static bool create(size_t size);
static bool is_created();
- static ObjectSampler* sampler();
static void destroy();
- // For operations that require exclusive access (non-safepoint)
- static ObjectSampler* acquire();
- static void release();
-
- // Stacktrace
- static void fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread);
- traceid stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread);
-
// Sampling
static void sample(HeapWord* object, size_t size, JavaThread* thread);
- void add(HeapWord* object, size_t size, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread);
+ void add(HeapWord* object, size_t size, traceid thread_id, JavaThread* thread);
void scavenge();
void remove_dead(ObjectSample* sample);
@@ -87,8 +72,15 @@
const ObjectSample* item_at(int index) const;
ObjectSample* item_at(int index);
int item_count() const;
+
+ public:
+ static ObjectSampler* sampler();
+ // For operations that require exclusive access (non-safepoint)
+ static ObjectSampler* acquire();
+ static void release();
+
const ObjectSample* first() const;
- const ObjectSample* last() const;
+ ObjectSample* last() const;
const ObjectSample* last_resolved() const;
void set_last_resolved(const ObjectSample* sample);
const JfrTicks& last_sweep() const;
--- a/src/hotspot/share/jfr/leakprofiler/sampling/sampleList.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/sampleList.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -50,12 +50,12 @@
SampleList(size_t limit, size_t cache_size = 0);
~SampleList();
- void set_last_resolved(const ObjectSample* sample);
ObjectSample* get();
+ ObjectSample* first() const;
ObjectSample* last() const;
- ObjectSample* first() const;
+ const ObjectSample* last_resolved() const;
+ void set_last_resolved(const ObjectSample* sample);
void release(ObjectSample* sample);
- const ObjectSample* last_resolved() const;
ObjectSample* reuse(ObjectSample* sample);
bool is_full() const;
size_t count() const;
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp Thu Sep 12 15:04:00 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.
- *
- * 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.
- *
- */
-
-#include "precompiled.hpp"
-#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp"
-#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
-
-JfrCheckpointBlob::JfrCheckpointBlob(const u1* checkpoint, size_t size) :
- _checkpoint(JfrCHeapObj::new_array<u1>(size)),
- _size(size),
- _next(),
- _written(false) {
- assert(checkpoint != NULL, "invariant");
- assert(_checkpoint != NULL, "invariant");
- memcpy(const_cast<u1*>(_checkpoint), checkpoint, size);
-}
-
-JfrCheckpointBlob::~JfrCheckpointBlob() {
- JfrCHeapObj::free(const_cast<u1*>(_checkpoint), _size);
-}
-
-const JfrCheckpointBlobHandle& JfrCheckpointBlob::next() const {
- return _next;
-}
-
-void JfrCheckpointBlob::write_this(JfrCheckpointWriter& writer) const {
- writer.bytes(_checkpoint, _size);
-}
-
-void JfrCheckpointBlob::exclusive_write(JfrCheckpointWriter& writer) const {
- if (!_written) {
- write_this(writer);
- _written = true;
- }
- if (_next.valid()) {
- _next->exclusive_write(writer);
- }
-}
-
-void JfrCheckpointBlob::write(JfrCheckpointWriter& writer) const {
- write_this(writer);
- if (_next.valid()) {
- _next->write(writer);
- }
-}
-
-void JfrCheckpointBlob::reset_write_state() const {
- if (_written) {
- _written = false;
- }
- if (_next.valid()) {
- _next->reset_write_state();
- }
-}
-
-void JfrCheckpointBlob::set_next(const JfrCheckpointBlobHandle& ref) {
- if (_next == ref) {
- return;
- }
- assert(_next != ref, "invariant");
- if (_next.valid()) {
- _next->set_next(ref);
- return;
- }
- _next = ref;
-}
-
-JfrCheckpointBlobHandle JfrCheckpointBlob::make(const u1* checkpoint, size_t size) {
- const JfrCheckpointBlob* cp_blob = new JfrCheckpointBlob(checkpoint, size);
- assert(cp_blob != NULL, "invariant");
- return JfrCheckpointBlobReference::make(cp_blob);
-}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp Thu Sep 12 15:04:00 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016, 2019, 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.
- *
- * 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.
- *
- */
-
-#ifndef SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP
-#define SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP
-
-#include "jfr/utilities/jfrAllocation.hpp"
-#include "jfr/utilities/jfrRefCountPointer.hpp"
-
-class JfrCheckpointBlob;
-class JfrCheckpointWriter;
-
-typedef RefCountPointer<JfrCheckpointBlob, MultiThreadedRefCounter> JfrCheckpointBlobReference;
-typedef RefCountHandle<JfrCheckpointBlobReference> JfrCheckpointBlobHandle;
-
-class JfrCheckpointBlob : public JfrCHeapObj {
- template <typename, typename>
- friend class RefCountPointer;
- private:
- const u1* _checkpoint;
- const size_t _size;
- JfrCheckpointBlobHandle _next;
- mutable bool _written;
-
- JfrCheckpointBlob(const u1* checkpoint, size_t size);
- ~JfrCheckpointBlob();
- const JfrCheckpointBlobHandle& next() const;
- void write_this(JfrCheckpointWriter& writer) const;
-
- public:
- void write(JfrCheckpointWriter& writer) const;
- void exclusive_write(JfrCheckpointWriter& writer) const;
- void reset_write_state() const;
- void set_next(const JfrCheckpointBlobHandle& ref);
- static JfrCheckpointBlobHandle make(const u1* checkpoint, size_t size);
-};
-
-#endif // SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -37,6 +37,7 @@
#include "jfr/utilities/jfrTypes.hpp"
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
+#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.inline.hpp"
@@ -87,22 +88,18 @@
static const size_t checkpoint_buffer_cache_count = 2;
static const size_t checkpoint_buffer_size = 512 * K;
-static JfrCheckpointMspace* create_mspace(size_t buffer_size, size_t limit, size_t cache_count, JfrCheckpointManager* system) {
- JfrCheckpointMspace* mspace = new JfrCheckpointMspace(buffer_size, limit, cache_count, system);
- if (mspace != NULL) {
- mspace->initialize();
- }
- return mspace;
+static JfrCheckpointMspace* allocate_mspace(size_t size, size_t limit, size_t cache_count, JfrCheckpointManager* mgr) {
+ return create_mspace<JfrCheckpointMspace, JfrCheckpointManager>(size, limit, cache_count, mgr);
}
bool JfrCheckpointManager::initialize() {
assert(_free_list_mspace == NULL, "invariant");
- _free_list_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this);
+ _free_list_mspace = allocate_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this);
if (_free_list_mspace == NULL) {
return false;
}
assert(_epoch_transition_mspace == NULL, "invariant");
- _epoch_transition_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this);
+ _epoch_transition_mspace = allocate_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this);
if (_epoch_transition_mspace == NULL) {
return false;
}
@@ -114,22 +111,6 @@
return JfrTypeManager::initialize();
}
-bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
- return _service_thread != thread && OrderAccess::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
-}
-
-void JfrCheckpointManager::synchronize_epoch() {
- assert(_checkpoint_epoch_state != JfrTraceIdEpoch::epoch(), "invariant");
- OrderAccess::storestore();
- _checkpoint_epoch_state = JfrTraceIdEpoch::epoch();
-}
-
-void JfrCheckpointManager::shift_epoch() {
- debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
- JfrTraceIdEpoch::shift_epoch();
- assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
-}
-
void JfrCheckpointManager::register_service_thread(const Thread* thread) {
_service_thread = thread;
}
@@ -151,7 +132,6 @@
}
#ifdef ASSERT
-
bool JfrCheckpointManager::is_locked() const {
return _lock->owned_by_self();
}
@@ -167,7 +147,6 @@
assert(buffer->lease(), "invariant");
assert(buffer->acquired_by_self(), "invariant");
}
-
#endif // ASSERT
static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t retry_count, Thread* thread) {
@@ -185,6 +164,10 @@
return buffer;
}
+bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
+ return _service_thread != thread && OrderAccess::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
+}
+
static const size_t lease_retry = 10;
BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */) {
@@ -256,33 +239,33 @@
return read_data<juint>(data + types_offset);
}
-static void write_checkpoint_header(JfrChunkWriter& cw, intptr_t offset_prev_cp_event, const u1* data) {
+static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) {
cw.reserve(sizeof(u4));
- cw.write((u8)EVENT_CHECKPOINT);
+ cw.write<u8>(EVENT_CHECKPOINT);
cw.write(starttime(data));
cw.write(duration(data));
- cw.write((jlong)offset_prev_cp_event);
+ cw.write(offset_prev_cp_event);
cw.write(is_flushpoint(data));
cw.write(number_of_types(data));
}
static void write_checkpoint_content(JfrChunkWriter& cw, const u1* data, size_t size) {
assert(data != NULL, "invariant");
- cw.write_unbuffered(data + payload_offset, size);
+ cw.write_unbuffered(data + payload_offset, size - sizeof(JfrCheckpointEntry));
}
static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) {
assert(data != NULL, "invariant");
- const intptr_t previous_checkpoint_event = cw.previous_checkpoint_offset();
- const intptr_t event_begin = cw.current_offset();
- const intptr_t offset_to_previous_checkpoint_event = 0 == previous_checkpoint_event ? 0 : previous_checkpoint_event - event_begin;
- const jlong total_checkpoint_size = total_size(data);
- write_checkpoint_header(cw, offset_to_previous_checkpoint_event, data);
- write_checkpoint_content(cw, data, total_checkpoint_size - sizeof(JfrCheckpointEntry));
- const jlong checkpoint_event_size = cw.current_offset() - event_begin;
- cw.write_padded_at_offset<u4>(checkpoint_event_size, event_begin);
- cw.set_previous_checkpoint_offset(event_begin);
- return (size_t)total_checkpoint_size;
+ const int64_t event_begin = cw.current_offset();
+ const int64_t last_checkpoint_event = cw.last_checkpoint_offset();
+ const int64_t delta = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin;
+ const int64_t checkpoint_size = total_size(data);
+ write_checkpoint_header(cw, delta, data);
+ write_checkpoint_content(cw, data, checkpoint_size);
+ const int64_t event_size = cw.current_offset() - event_begin;
+ cw.write_padded_at_offset<u4>(event_size, event_begin);
+ cw.set_last_checkpoint_offset(event_begin);
+ return (size_t)checkpoint_size;
}
static size_t write_checkpoints(JfrChunkWriter& cw, const u1* data, size_t size) {
@@ -290,14 +273,14 @@
assert(data != NULL, "invariant");
assert(size > 0, "invariant");
const u1* const limit = data + size;
- const u1* next_entry = data;
+ const u1* next = data;
size_t processed = 0;
- while (next_entry < limit) {
- const size_t checkpoint_size = write_checkpoint_event(cw, next_entry);
+ while (next < limit) {
+ const size_t checkpoint_size = write_checkpoint_event(cw, next);
processed += checkpoint_size;
- next_entry += checkpoint_size;
+ next += checkpoint_size;
}
- assert(next_entry == limit, "invariant");
+ assert(next == limit, "invariant");
return processed;
}
@@ -331,6 +314,12 @@
return wo.processed();
}
+void JfrCheckpointManager::synchronize_epoch() {
+ assert(_checkpoint_epoch_state != JfrTraceIdEpoch::epoch(), "invariant");
+ OrderAccess::storestore();
+ _checkpoint_epoch_state = JfrTraceIdEpoch::epoch();
+}
+
size_t JfrCheckpointManager::write() {
const size_t processed = write_mspace<MutexedWriteOp, CompositeOperation>(_free_list_mspace, _chunkwriter);
synchronize_epoch();
@@ -372,10 +361,16 @@
JfrTypeManager::write_type_set_for_unloaded_classes();
}
-void JfrCheckpointManager::create_thread_checkpoint(JavaThread* jt) {
- JfrTypeManager::create_thread_checkpoint(jt);
+void JfrCheckpointManager::create_thread_blob(JavaThread* jt) {
+ JfrTypeManager::create_thread_blob(jt);
}
void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) {
JfrTypeManager::write_thread_checkpoint(jt);
}
+
+void JfrCheckpointManager::shift_epoch() {
+ debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
+ JfrTraceIdEpoch::shift_epoch();
+ assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -92,7 +92,7 @@
public:
void register_service_thread(const Thread* t);
static void write_type_set_for_unloaded_classes();
- static void create_thread_checkpoint(JavaThread* jt);
+ static void create_thread_blob(JavaThread* jt);
static void write_thread_checkpoint(JavaThread* jt);
friend class JfrRecorder;
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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,6 +25,7 @@
#include "precompiled.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/utilities/jfrBlob.hpp"
#include "jfr/writers/jfrBigEndianWriter.hpp"
JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) :
@@ -126,7 +127,7 @@
write_padded_at_offset(nof_entries, offset);
}
-const u1* JfrCheckpointWriter::session_data(size_t* size, const JfrCheckpointContext* ctx /* 0 */) {
+const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */, const JfrCheckpointContext* ctx /* 0 */) {
assert(this->is_acquired(), "wrong state!");
if (!this->is_valid()) {
*size = 0;
@@ -140,8 +141,10 @@
*size = this->used_size();
assert(this->start_pos() + *size == this->current_pos(), "invariant");
write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count());
- this->seek(_offset + (_header ? sizeof(JfrCheckpointEntry) : 0));
- set_count(0);
+ _header = false; // the header was just written
+ if (move) {
+ this->seek(_offset);
+ }
return this->start_pos();
}
@@ -160,26 +163,19 @@
return this->used_size() > sizeof(JfrCheckpointEntry);
}
-JfrCheckpointBlobHandle JfrCheckpointWriter::checkpoint_blob() {
+JfrBlobHandle JfrCheckpointWriter::copy(const JfrCheckpointContext* ctx /* 0 */) {
size_t size = 0;
- const u1* data = session_data(&size);
- return JfrCheckpointBlob::make(data, size);
+ const u1* data = session_data(&size, false, ctx);
+ return JfrBlob::make(data, size);
}
-JfrCheckpointBlobHandle JfrCheckpointWriter::copy(const JfrCheckpointContext* ctx /* 0 */) {
- if (ctx == NULL) {
- return checkpoint_blob();
- }
+JfrBlobHandle JfrCheckpointWriter::move(const JfrCheckpointContext* ctx /* 0 */) {
size_t size = 0;
- const u1* data = session_data(&size, ctx);
- return JfrCheckpointBlob::make(data, size);
-}
-
-JfrCheckpointBlobHandle JfrCheckpointWriter::move(const JfrCheckpointContext* ctx /* 0 */) {
- JfrCheckpointBlobHandle data = copy(ctx);
+ const u1* data = session_data(&size, true, ctx);
+ JfrBlobHandle blob = JfrBlob::make(data, size);
if (ctx != NULL) {
const_cast<JfrCheckpointContext*>(ctx)->count = 0;
set_context(*ctx);
}
- return data;
+ return blob;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,8 +25,8 @@
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP
#define SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP
-#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp"
#include "jfr/recorder/storage/jfrBuffer.hpp"
+#include "jfr/utilities/jfrBlob.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "jfr/writers/jfrEventWriterHost.inline.hpp"
@@ -67,9 +67,8 @@
void increment();
void set_flushpoint(bool flushpoint);
bool is_flushpoint() const;
- const u1* session_data(size_t* size, const JfrCheckpointContext* ctx = NULL);
+ const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL);
void release();
-
public:
JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread);
~JfrCheckpointWriter();
@@ -80,9 +79,8 @@
const JfrCheckpointContext context() const;
void set_context(const JfrCheckpointContext ctx);
bool has_data() const;
- JfrCheckpointBlobHandle checkpoint_blob();
- JfrCheckpointBlobHandle copy(const JfrCheckpointContext* ctx = NULL);
- JfrCheckpointBlobHandle move(const JfrCheckpointContext* ctx = NULL);
+ JfrBlobHandle copy(const JfrCheckpointContext* ctx = NULL);
+ JfrBlobHandle move(const JfrCheckpointContext* ctx = NULL);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -30,7 +30,6 @@
#include "gc/shared/gcName.hpp"
#include "gc/shared/gcTrace.hpp"
#include "gc/shared/gcWhen.hpp"
-#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/leakprofiler/leakProfiler.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
@@ -275,34 +274,26 @@
class TypeSetSerialization {
private:
+ JfrCheckpointWriter* _leakp_writer;
bool _class_unload;
public:
- explicit TypeSetSerialization(bool class_unload) : _class_unload(class_unload) {}
- void write(JfrCheckpointWriter& writer, JfrCheckpointWriter* leakp_writer) {
- JfrTypeSet::serialize(&writer, leakp_writer, _class_unload);
+ TypeSetSerialization(bool class_unload, JfrCheckpointWriter* leakp_writer = NULL) :
+ _leakp_writer(leakp_writer), _class_unload(class_unload) {}
+ void write(JfrCheckpointWriter& writer) {
+ JfrTypeSet::serialize(&writer, _leakp_writer, _class_unload);
}
};
void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) {
TypeSetSerialization type_set(true);
- if (LeakProfiler::is_running()) {
- JfrCheckpointWriter leakp_writer(false, true, Thread::current());
- type_set.write(writer, &leakp_writer);
- ObjectSampleCheckpoint::install(leakp_writer, true, true);
- return;
- }
- type_set.write(writer, NULL);
+ type_set.write(writer);
};
+TypeSet::TypeSet(JfrCheckpointWriter* leakp_writer) : _leakp_writer(leakp_writer) {}
+
void TypeSet::serialize(JfrCheckpointWriter& writer) {
- TypeSetSerialization type_set(false);
- if (LeakProfiler::is_running()) {
- JfrCheckpointWriter leakp_writer(false, true, Thread::current());
- type_set.write(writer, &leakp_writer);
- ObjectSampleCheckpoint::install(leakp_writer, false, true);
- return;
- }
- type_set.write(writer, NULL);
+ TypeSetSerialization type_set(false, _leakp_writer);
+ type_set.write(writer);
};
void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) {
@@ -313,7 +304,6 @@
assert(_thread != NULL, "invariant");
assert(_thread == Thread::current(), "invariant");
assert(_thread->is_Java_thread(), "invariant");
- assert(!_thread->jfr_thread_local()->has_thread_checkpoint(), "invariant");
ResourceMark rm(_thread);
const oop threadObj = _thread->threadObj();
assert(threadObj != NULL, "invariant");
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -108,7 +108,10 @@
};
class TypeSet : public JfrSerializer {
+ private:
+ JfrCheckpointWriter* _leakp_writer;
public:
+ explicit TypeSet(JfrCheckpointWriter* leakp_writer = NULL);
void serialize(JfrCheckpointWriter& writer);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -23,12 +23,17 @@
*/
#include "precompiled.hpp"
+#include "jfr/jfr.hpp"
+#include "jfr/leakprofiler/leakProfiler.hpp"
+#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/metadata/jfrSerializer.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
#include "jfr/utilities/jfrDoublyLinkedList.hpp"
#include "jfr/utilities/jfrIterator.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/handles.inline.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"
@@ -39,7 +44,7 @@
JfrSerializerRegistration* _next;
JfrSerializerRegistration* _prev;
JfrSerializer* _serializer;
- mutable JfrCheckpointBlobHandle _cache;
+ mutable JfrBlobHandle _cache;
JfrTypeId _id;
bool _permit_cache;
@@ -148,45 +153,59 @@
}
void JfrTypeManager::write_type_set() {
- // can safepoint here because of Module_lock
- MutexLocker cld_lock(SafepointSynchronize::is_at_safepoint() ? NULL : ClassLoaderDataGraph_lock);
- MutexLocker lock(SafepointSynchronize::is_at_safepoint() ? NULL : Module_lock);
-
- JfrCheckpointWriter writer(true, true, Thread::current());
- TypeSet set;
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+ // can safepoint here
+ MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+ MutexLocker module_lock(Module_lock);
+ if (!LeakProfiler::is_running()) {
+ JfrCheckpointWriter writer(true, true, Thread::current());
+ TypeSet set;
+ set.serialize(writer);
+ return;
+ }
+ JfrCheckpointWriter leakp_writer(false, true, Thread::current());
+ JfrCheckpointWriter writer(false, true, Thread::current());
+ TypeSet set(&leakp_writer);
set.serialize(writer);
+ ObjectSampleCheckpoint::on_type_set(leakp_writer);
}
void JfrTypeManager::write_type_set_for_unloaded_classes() {
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
JfrCheckpointWriter writer(false, true, Thread::current());
+ const JfrCheckpointContext ctx = writer.context();
ClassUnloadTypeSet class_unload_set;
class_unload_set.serialize(writer);
+ if (LeakProfiler::is_running()) {
+ ObjectSampleCheckpoint::on_type_set_unload(writer);
+ }
+ if (!Jfr::is_recording()) {
+ // discard anything written
+ writer.set_context(ctx);
+ }
}
-void JfrTypeManager::create_thread_checkpoint(JavaThread* jt) {
+void JfrTypeManager::create_thread_blob(JavaThread* jt) {
assert(jt != NULL, "invariant");
+ ResourceMark rm(jt);
+ HandleMark hm(jt);
JfrThreadConstant type_thread(jt);
JfrCheckpointWriter writer(false, true, jt);
writer.write_type(TYPE_THREAD);
type_thread.serialize(writer);
// create and install a checkpoint blob
- jt->jfr_thread_local()->set_thread_checkpoint(writer.checkpoint_blob());
- assert(jt->jfr_thread_local()->has_thread_checkpoint(), "invariant");
+ jt->jfr_thread_local()->set_thread_blob(writer.move());
+ assert(jt->jfr_thread_local()->has_thread_blob(), "invariant");
}
void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) {
assert(jt != NULL, "JavaThread is NULL!");
ResourceMark rm(jt);
- if (jt->jfr_thread_local()->has_thread_checkpoint()) {
- JfrCheckpointWriter writer(false, false, jt);
- jt->jfr_thread_local()->thread_checkpoint()->write(writer);
- } else {
- JfrThreadConstant type_thread(jt);
- JfrCheckpointWriter writer(false, true, jt);
- writer.write_type(TYPE_THREAD);
- type_thread.serialize(writer);
- }
+ HandleMark hm(jt);
+ JfrThreadConstant type_thread(jt);
+ JfrCheckpointWriter writer(false, true, jt);
+ writer.write_type(TYPE_THREAD);
+ type_thread.serialize(writer);
}
#ifdef ASSERT
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -37,7 +37,7 @@
static void write_safepoint_types(JfrCheckpointWriter& writer);
static void write_type_set();
static void write_type_set_for_unloaded_classes();
- static void create_thread_checkpoint(JavaThread* jt);
+ static void create_thread_blob(JavaThread* jt);
static void write_thread_checkpoint(JavaThread* jt);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -28,32 +28,22 @@
#include "classfile/moduleEntry.hpp"
#include "classfile/packageEntry.hpp"
#include "classfile/symbolTable.hpp"
-#include "classfile/systemDictionary.hpp"
#include "jfr/jfr.hpp"
#include "jfr/jni/jfrGetAllEventClasses.hpp"
-#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp"
#include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp"
-#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
-#include "jfr/recorder/storage/jfrBuffer.hpp"
#include "jfr/utilities/jfrHashtable.hpp"
#include "jfr/utilities/jfrTypes.hpp"
+#include "jfr/writers/jfrTypeWriterHost.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
-#include "memory/resourceArea.hpp"
#include "utilities/accessFlags.hpp"
-// incremented on each checkpoint
-static u8 checkpoint_id = 0;
-
-// creates a unique id by combining a checkpoint relative symbol id (2^24)
-// with the current checkpoint id (2^40)
-#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id)))
-
typedef const Klass* KlassPtr;
typedef const PackageEntry* PkgPtr;
typedef const ModuleEntry* ModPtr;
@@ -63,57 +53,112 @@
typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr;
typedef const JfrSymbolId::CStringEntry* CStringEntryPtr;
-static traceid module_id(PkgPtr pkg) {
- assert(pkg != NULL, "invariant");
- ModPtr module_entry = pkg->module();
- return module_entry != NULL && module_entry->is_named() ? TRACE_ID(module_entry) : 0;
+static JfrCheckpointWriter* _writer = NULL;
+static JfrCheckpointWriter* _leakp_writer = NULL;
+static JfrArtifactSet* _artifacts = NULL;
+static JfrArtifactClosure* _subsystem_callback = NULL;
+static bool _class_unload = false;
+static bool _flushpoint = false;
+
+// incremented on each rotation
+static u8 checkpoint_id = 1;
+
+// creates a unique id by combining a checkpoint relative symbol id (2^24)
+// with the current checkpoint id (2^40)
+#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id)))
+
+static traceid create_symbol_id(traceid artifact_id) {
+ return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0;
+}
+
+static bool current_epoch() {
+ return _class_unload;
}
-static traceid package_id(KlassPtr klass) {
- assert(klass != NULL, "invariant");
- PkgPtr pkg_entry = klass->package();
- return pkg_entry == NULL ? 0 : TRACE_ID(pkg_entry);
+static bool previous_epoch() {
+ return !current_epoch();
+}
+
+static bool is_complete() {
+ return !_artifacts->has_klass_entries() && current_epoch();
+}
+
+static traceid mark_symbol(KlassPtr klass, bool leakp) {
+ return klass != NULL ? create_symbol_id(_artifacts->mark(klass, leakp)) : 0;
}
-static traceid cld_id(CldPtr cld) {
- assert(cld != NULL, "invariant");
- return cld->is_unsafe_anonymous() ? 0 : TRACE_ID(cld);
+static traceid mark_symbol(Symbol* symbol, bool leakp) {
+ return symbol != NULL ? create_symbol_id(_artifacts->mark(symbol, leakp)) : 0;
+}
+
+static traceid get_bootstrap_name(bool leakp) {
+ return create_symbol_id(_artifacts->bootstrap_name(leakp));
+}
+
+template <typename T>
+static traceid artifact_id(const T* ptr) {
+ assert(ptr != NULL, "invariant");
+ return TRACE_ID(ptr);
}
-static void tag_leakp_klass_artifacts(KlassPtr k, bool class_unload) {
- assert(k != NULL, "invariant");
- PkgPtr pkg = k->package();
- if (pkg != NULL) {
- tag_leakp_artifact(pkg, class_unload);
- ModPtr module = pkg->module();
- if (module != NULL) {
- tag_leakp_artifact(module, class_unload);
- }
+static traceid package_id(KlassPtr klass, bool leakp) {
+ assert(klass != NULL, "invariant");
+ PkgPtr pkg_entry = klass->package();
+ if (pkg_entry == NULL) {
+ return 0;
+ }
+ if (leakp) {
+ SET_LEAKP(pkg_entry);
}
- CldPtr cld = k->class_loader_data();
- assert(cld != NULL, "invariant");
- if (!cld->is_unsafe_anonymous()) {
- tag_leakp_artifact(cld, class_unload);
+ // package implicitly tagged already
+ return artifact_id(pkg_entry);
+}
+
+static traceid module_id(PkgPtr pkg, bool leakp) {
+ assert(pkg != NULL, "invariant");
+ ModPtr module_entry = pkg->module();
+ if (module_entry == NULL || !module_entry->is_named()) {
+ return 0;
}
+ if (leakp) {
+ SET_LEAKP(module_entry);
+ } else {
+ SET_TRANSIENT(module_entry);
+ }
+ return artifact_id(module_entry);
}
-class TagLeakpKlassArtifact {
- bool _class_unload;
- public:
- TagLeakpKlassArtifact(bool class_unload) : _class_unload(class_unload) {}
- bool operator()(KlassPtr klass) {
- if (_class_unload) {
- if (LEAKP_USED_THIS_EPOCH(klass)) {
- tag_leakp_klass_artifacts(klass, _class_unload);
- }
- } else {
- if (LEAKP_USED_PREV_EPOCH(klass)) {
- tag_leakp_klass_artifacts(klass, _class_unload);
- }
- }
- return true;
+static traceid method_id(KlassPtr klass, MethodPtr method) {
+ assert(klass != NULL, "invariant");
+ assert(method != NULL, "invariant");
+ return METHOD_ID(klass, method);
+}
+
+static traceid cld_id(CldPtr cld, bool leakp) {
+ assert(cld != NULL, "invariant");
+ if (cld->is_unsafe_anonymous()) {
+ return 0;
+ }
+ if (leakp) {
+ SET_LEAKP(cld);
+ } else {
+ SET_TRANSIENT(cld);
}
-};
+ return artifact_id(cld);
+}
+
+template <typename T>
+static s4 get_flags(const T* ptr) {
+ assert(ptr != NULL, "invariant");
+ return ptr->access_flags().get_flags();
+}
+
+template <typename T>
+static void set_serialized(const T* ptr) {
+ assert(ptr != NULL, "invariant");
+ SET_SERIALIZED(ptr);
+ assert(IS_SERIALIZED(ptr), "invariant");
+}
/*
* In C++03, functions used as template parameters must have external linkage;
@@ -123,11 +168,10 @@
* The weird naming is an effort to decrease the risk of name clashes.
*/
-int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) {
+static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp) {
assert(writer != NULL, "invariant");
- assert(artifacts != NULL, "invariant");
- assert(k != NULL, "invariant");
- KlassPtr klass = (KlassPtr)k;
+ assert(_artifacts != NULL, "invariant");
+ assert(klass != NULL, "invariant");
traceid pkg_id = 0;
KlassPtr theklass = klass;
if (theklass->is_objArray_klass()) {
@@ -135,445 +179,167 @@
theklass = obj_arr_klass->bottom_klass();
}
if (theklass->is_instance_klass()) {
- pkg_id = package_id(theklass);
+ pkg_id = package_id(theklass, leakp);
} else {
assert(theklass->is_typeArray_klass(), "invariant");
}
- const traceid symbol_id = artifacts->mark(klass);
- assert(symbol_id > 0, "need to have an address for symbol!");
- writer->write(TRACE_ID(klass));
- writer->write(cld_id(klass->class_loader_data()));
- writer->write((traceid)CREATE_SYMBOL_ID(symbol_id));
+ writer->write(artifact_id(klass));
+ writer->write(cld_id(klass->class_loader_data(), leakp));
+ writer->write(mark_symbol(klass, leakp));
writer->write(pkg_id);
- writer->write((s4)klass->access_flags().get_flags());
+ writer->write(get_flags(klass));
return 1;
}
-typedef LeakPredicate<KlassPtr> LeakKlassPredicate;
-typedef JfrPredicatedArtifactWriterImplHost<KlassPtr, LeakKlassPredicate, write__artifact__klass> LeakKlassWriterImpl;
-typedef JfrArtifactWriterHost<LeakKlassWriterImpl, TYPE_CLASS> LeakKlassWriter;
-typedef JfrArtifactWriterImplHost<KlassPtr, write__artifact__klass> KlassWriterImpl;
-typedef JfrArtifactWriterHost<KlassWriterImpl, TYPE_CLASS> KlassWriter;
+int write__klass(JfrCheckpointWriter* writer, const void* k) {
+ assert(k != NULL, "invariant");
+ KlassPtr klass = (KlassPtr)k;
+ set_serialized(klass);
+ return write_klass(writer, klass, false);
+}
+
+int write__klass__leakp(JfrCheckpointWriter* writer, const void* k) {
+ assert(k != NULL, "invariant");
+ KlassPtr klass = (KlassPtr)k;
+ return write_klass(writer, klass, true);
+}
+
+static void do_implied(Klass* klass) {
+ assert(klass != NULL, "invariant");
+ if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
+ if (_leakp_writer != NULL) {
+ SET_LEAKP(klass);
+ }
+ _subsystem_callback->do_artifact(klass);
+ }
+}
-int write__artifact__method(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
- assert(writer != NULL, "invariant");
- assert(artifacts != NULL, "invariant");
- assert(m != NULL, "invariant");
- MethodPtr method = (MethodPtr)m;
- const traceid method_name_symbol_id = artifacts->mark(method->name());
- assert(method_name_symbol_id > 0, "invariant");
- const traceid method_sig_symbol_id = artifacts->mark(method->signature());
- assert(method_sig_symbol_id > 0, "invariant");
- KlassPtr klass = method->method_holder();
+static void do_unloaded_klass(Klass* klass) {
+ assert(klass != NULL, "invariant");
+ assert(_subsystem_callback != NULL, "invariant");
+ if (IS_JDK_JFR_EVENT_SUBKLASS(klass)) {
+ JfrEventClasses::increment_unloaded_event_class();
+ }
+ if (USED_THIS_EPOCH(klass)) {
+ ObjectSampleCheckpoint::on_klass_unload(klass);
+ _subsystem_callback->do_artifact(klass);
+ return;
+ }
+ do_implied(klass);
+}
+
+static void do_klass(Klass* klass) {
assert(klass != NULL, "invariant");
- assert(METHOD_USED_ANY_EPOCH(klass), "invariant");
- writer->write((u8)METHOD_ID(klass, method));
- writer->write((u8)TRACE_ID(klass));
- writer->write((u8)CREATE_SYMBOL_ID(method_name_symbol_id));
- writer->write((u8)CREATE_SYMBOL_ID(method_sig_symbol_id));
- writer->write((u2)method->access_flags().get_flags());
- writer->write(const_cast<Method*>(method)->is_hidden() ? (u1)1 : (u1)0);
- return 1;
+ assert(_subsystem_callback != NULL, "invariant");
+ if (current_epoch()) {
+ if (USED_THIS_EPOCH(klass)) {
+ _subsystem_callback->do_artifact(klass);
+ return;
+ }
+ } else {
+ if (USED_PREV_EPOCH(klass)) {
+ _subsystem_callback->do_artifact(klass);
+ return;
+ }
+ }
+ do_implied(klass);
+}
+
+static void do_klasses() {
+ if (_class_unload) {
+ ClassLoaderDataGraph::classes_unloading_do(&do_unloaded_klass);
+ return;
+ }
+ ClassLoaderDataGraph::classes_do(&do_klass);
}
-typedef JfrArtifactWriterImplHost<MethodPtr, write__artifact__method> MethodWriterImplTarget;
-typedef JfrArtifactWriterHost<MethodWriterImplTarget, TYPE_METHOD> MethodWriterImpl;
+typedef SerializePredicate<KlassPtr> KlassPredicate;
+typedef JfrPredicatedTypeWriterImplHost<KlassPtr, KlassPredicate, write__klass> KlassWriterImpl;
+typedef JfrTypeWriterHost<KlassWriterImpl, TYPE_CLASS> KlassWriter;
+typedef CompositeFunctor<KlassPtr, KlassWriter, KlassArtifactRegistrator> KlassWriterRegistration;
+typedef JfrArtifactCallbackHost<KlassPtr, KlassWriterRegistration> KlassCallback;
+
+typedef LeakPredicate<KlassPtr> LeakKlassPredicate;
+typedef JfrPredicatedTypeWriterImplHost<KlassPtr, LeakKlassPredicate, write__klass__leakp> LeakKlassWriterImpl;
+typedef JfrTypeWriterHost<LeakKlassWriterImpl, TYPE_CLASS> LeakKlassWriter;
+
+typedef CompositeFunctor<KlassPtr, LeakKlassWriter, KlassWriter> CompositeKlassWriter;
+typedef CompositeFunctor<KlassPtr, CompositeKlassWriter, KlassArtifactRegistrator> CompositeKlassWriterRegistration;
+typedef JfrArtifactCallbackHost<KlassPtr, CompositeKlassWriterRegistration> CompositeKlassCallback;
-int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) {
+static bool write_klasses() {
+ assert(!_artifacts->has_klass_entries(), "invariant");
+ assert(_writer != NULL, "invariant");
+ KlassArtifactRegistrator reg(_artifacts);
+ KlassWriter kw(_writer, _class_unload);
+ KlassWriterRegistration kwr(&kw, ®);
+ if (_leakp_writer == NULL) {
+ KlassCallback callback(&kwr);
+ _subsystem_callback = &callback;
+ do_klasses();
+ } else {
+ LeakKlassWriter lkw(_leakp_writer, _artifacts, _class_unload);
+ CompositeKlassWriter ckw(&lkw, &kw);
+ CompositeKlassWriterRegistration ckwr(&ckw, ®);
+ CompositeKlassCallback callback(&ckwr);
+ _subsystem_callback = &callback;
+ do_klasses();
+ }
+ if (is_complete()) {
+ return false;
+ }
+ _artifacts->tally(kw);
+ return true;
+}
+
+template <typename T>
+static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) {
+ assert(callback != NULL, "invariant");
+ assert(value != NULL, "invariant");
+ if (USED_PREV_EPOCH(value)) {
+ callback->do_artifact(value);
+ assert(IS_NOT_SERIALIZED(value), "invariant");
+ return;
+ }
+ if (IS_SERIALIZED(value)) {
+ CLEAR_SERIALIZED(value);
+ }
+ assert(IS_NOT_SERIALIZED(value), "invariant");
+}
+
+static int write_package(JfrCheckpointWriter* writer, PkgPtr pkg, bool leakp) {
assert(writer != NULL, "invariant");
- assert(artifacts != NULL, "invariant");
- assert(p != NULL, "invariant");
- PkgPtr pkg = (PkgPtr)p;
- Symbol* const pkg_name = pkg->name();
- const traceid package_name_symbol_id = pkg_name != NULL ? artifacts->mark(pkg_name) : 0;
- assert(package_name_symbol_id > 0, "invariant");
- writer->write((traceid)TRACE_ID(pkg));
- writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id));
- writer->write(module_id(pkg));
+ assert(_artifacts != NULL, "invariant");
+ assert(pkg != NULL, "invariant");
+ writer->write(artifact_id(pkg));
+ writer->write(mark_symbol(pkg->name(), leakp));
+ writer->write(module_id(pkg, leakp));
writer->write((bool)pkg->is_exported());
return 1;
}
-typedef LeakPredicate<PkgPtr> LeakPackagePredicate;
-int _compare_pkg_ptr_(PkgPtr const& lhs, PkgPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<PkgPtr, _compare_pkg_ptr_> PackagePredicate;
-typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, LeakPackagePredicate, write__artifact__package> LeakPackageWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, PackagePredicate, write__artifact__package> PackageWriterImpl;
-typedef JfrArtifactWriterHost<LeakPackageWriterImpl, TYPE_PACKAGE> LeakPackageWriter;
-typedef JfrArtifactWriterHost<PackageWriterImpl, TYPE_PACKAGE> PackageWriter;
-
-int write__artifact__module(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
- assert( m != NULL, "invariant");
- ModPtr entry = (ModPtr)m;
- Symbol* const module_name = entry->name();
- const traceid module_name_symbol_id = module_name != NULL ? artifacts->mark(module_name) : 0;
- Symbol* const module_version = entry->version();
- const traceid module_version_symbol_id = module_version != NULL ? artifacts->mark(module_version) : 0;
- Symbol* const module_location = entry->location();
- const traceid module_location_symbol_id = module_location != NULL ? artifacts->mark(module_location) : 0;
- writer->write((traceid)TRACE_ID(entry));
- writer->write(module_name_symbol_id == 0 ? (traceid)0 : (traceid)CREATE_SYMBOL_ID(module_name_symbol_id));
- writer->write(module_version_symbol_id == 0 ? (traceid)0 : (traceid)CREATE_SYMBOL_ID(module_version_symbol_id));
- writer->write(module_location_symbol_id == 0 ? (traceid)0 : (traceid)CREATE_SYMBOL_ID(module_location_symbol_id));
- writer->write(cld_id(entry->loader_data()));
- return 1;
-}
-
-typedef LeakPredicate<ModPtr> LeakModulePredicate;
-int _compare_mod_ptr_(ModPtr const& lhs, ModPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<ModPtr, _compare_mod_ptr_> ModulePredicate;
-typedef JfrPredicatedArtifactWriterImplHost<ModPtr, LeakModulePredicate, write__artifact__module> LeakModuleWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<ModPtr, ModulePredicate, write__artifact__module> ModuleWriterImpl;
-typedef JfrArtifactWriterHost<LeakModuleWriterImpl, TYPE_MODULE> LeakModuleWriter;
-typedef JfrArtifactWriterHost<ModuleWriterImpl, TYPE_MODULE> ModuleWriter;
-
-int write__artifact__classloader(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) {
- assert(c != NULL, "invariant");
- CldPtr cld = (CldPtr)c;
- assert(!cld->is_unsafe_anonymous(), "invariant");
- const traceid cld_id = TRACE_ID(cld);
- // class loader type
- const Klass* class_loader_klass = cld->class_loader_klass();
- if (class_loader_klass == NULL) {
- // (primordial) boot class loader
- writer->write(cld_id); // class loader instance id
- writer->write((traceid)0); // class loader type id (absence of)
- writer->write((traceid)CREATE_SYMBOL_ID(1)); // 1 maps to synthetic name -> "bootstrap"
- } else {
- Symbol* symbol_name = cld->name();
- const traceid symbol_name_id = symbol_name != NULL ? artifacts->mark(symbol_name) : 0;
- writer->write(cld_id); // class loader instance id
- writer->write(TRACE_ID(class_loader_klass)); // class loader type id
- writer->write(symbol_name_id == 0 ? (traceid)0 :
- (traceid)CREATE_SYMBOL_ID(symbol_name_id)); // class loader instance name
- }
- return 1;
-}
-
-typedef LeakPredicate<CldPtr> LeakCldPredicate;
-int _compare_cld_ptr_(CldPtr const& lhs, CldPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<CldPtr, _compare_cld_ptr_> CldPredicate;
-typedef JfrPredicatedArtifactWriterImplHost<CldPtr, LeakCldPredicate, write__artifact__classloader> LeakCldWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<CldPtr, CldPredicate, write__artifact__classloader> CldWriterImpl;
-typedef JfrArtifactWriterHost<LeakCldWriterImpl, TYPE_CLASSLOADER> LeakCldWriter;
-typedef JfrArtifactWriterHost<CldWriterImpl, TYPE_CLASSLOADER> CldWriter;
-
-typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr;
-
-static int write__artifact__symbol__entry__(JfrCheckpointWriter* writer,
- SymbolEntryPtr entry) {
- assert(writer != NULL, "invariant");
- assert(entry != NULL, "invariant");
- ResourceMark rm;
- writer->write(CREATE_SYMBOL_ID(entry->id()));
- writer->write(entry->value()->as_C_string());
- return 1;
-}
-
-int write__artifact__symbol__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) {
- assert(e != NULL, "invariant");
- return write__artifact__symbol__entry__(writer, (SymbolEntryPtr)e);
-}
-
-typedef JfrArtifactWriterImplHost<SymbolEntryPtr, write__artifact__symbol__entry> SymbolEntryWriterImpl;
-typedef JfrArtifactWriterHost<SymbolEntryWriterImpl, TYPE_SYMBOL> SymbolEntryWriter;
-
-typedef const JfrSymbolId::CStringEntry* CStringEntryPtr;
-
-static int write__artifact__cstring__entry__(JfrCheckpointWriter* writer, CStringEntryPtr entry) {
- assert(writer != NULL, "invariant");
- assert(entry != NULL, "invariant");
- writer->write(CREATE_SYMBOL_ID(entry->id()));
- writer->write(entry->value());
- return 1;
-}
-
-int write__artifact__cstring__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) {
- assert(e != NULL, "invariant");
- return write__artifact__cstring__entry__(writer, (CStringEntryPtr)e);
-}
-
-typedef JfrArtifactWriterImplHost<CStringEntryPtr, write__artifact__cstring__entry> CStringEntryWriterImpl;
-typedef JfrArtifactWriterHost<CStringEntryWriterImpl, TYPE_SYMBOL> CStringEntryWriter;
-
-int write__artifact__klass__symbol(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) {
- assert(writer != NULL, "invariant");
- assert(artifacts != NULL, "invaiant");
- assert(k != NULL, "invariant");
- const InstanceKlass* const ik = (const InstanceKlass*)k;
- if (ik->is_unsafe_anonymous()) {
- CStringEntryPtr entry =
- artifacts->map_cstring(JfrSymbolId::unsafe_anonymous_klass_name_hash_code(ik));
- assert(entry != NULL, "invariant");
- return write__artifact__cstring__entry__(writer, entry);
- }
-
- SymbolEntryPtr entry = artifacts->map_symbol(JfrSymbolId::regular_klass_name_hash_code(ik));
- return write__artifact__symbol__entry__(writer, entry);
-}
-
-int _compare_traceid_(const traceid& lhs, const traceid& rhs) {
- return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0;
+int write__package(JfrCheckpointWriter* writer, const void* p) {
+ assert(p != NULL, "invariant");
+ PkgPtr pkg = (PkgPtr)p;
+ set_serialized(pkg);
+ return write_package(writer, pkg, false);
}
-template <template <typename> class Predicate>
-class KlassSymbolWriterImpl {
- private:
- JfrCheckpointWriter* _writer;
- JfrArtifactSet* _artifacts;
- Predicate<KlassPtr> _predicate;
- MethodUsedPredicate<true> _method_used_predicate;
- MethodFlagPredicate _method_flag_predicate;
- UniquePredicate<traceid, _compare_traceid_> _unique_predicate;
-
- int klass_symbols(KlassPtr klass);
- int package_symbols(PkgPtr pkg);
- int module_symbols(ModPtr module);
- int class_loader_symbols(CldPtr cld);
- int method_symbols(KlassPtr klass);
-
- public:
- typedef KlassPtr Type;
- KlassSymbolWriterImpl(JfrCheckpointWriter* writer,
- JfrArtifactSet* artifacts,
- bool class_unload) : _writer(writer),
- _artifacts(artifacts),
- _predicate(class_unload),
- _method_used_predicate(class_unload),
- _method_flag_predicate(class_unload),
- _unique_predicate(class_unload) {}
-
- int operator()(KlassPtr klass) {
- assert(klass != NULL, "invariant");
- int count = 0;
- if (_predicate(klass)) {
- count += klass_symbols(klass);
- PkgPtr pkg = klass->package();
- if (pkg != NULL) {
- count += package_symbols(pkg);
- ModPtr module = pkg->module();
- if (module != NULL && module->is_named()) {
- count += module_symbols(module);
- }
- }
- CldPtr cld = klass->class_loader_data();
- assert(cld != NULL, "invariant");
- if (!cld->is_unsafe_anonymous()) {
- count += class_loader_symbols(cld);
- }
- if (_method_used_predicate(klass)) {
- count += method_symbols(klass);
- }
- }
- return count;
- }
-};
-
-template <template <typename> class Predicate>
-int KlassSymbolWriterImpl<Predicate>::klass_symbols(KlassPtr klass) {
- assert(klass != NULL, "invariant");
- assert(_predicate(klass), "invariant");
- const InstanceKlass* const ik = (const InstanceKlass*)klass;
- if (ik->is_unsafe_anonymous()) {
- CStringEntryPtr entry =
- this->_artifacts->map_cstring(JfrSymbolId::unsafe_anonymous_klass_name_hash_code(ik));
- assert(entry != NULL, "invariant");
- return _unique_predicate(entry->id()) ? write__artifact__cstring__entry__(this->_writer, entry) : 0;
- }
- SymbolEntryPtr entry = this->_artifacts->map_symbol(ik->name());
- assert(entry != NULL, "invariant");
- return _unique_predicate(entry->id()) ? write__artifact__symbol__entry__(this->_writer, entry) : 0;
-}
-
-template <template <typename> class Predicate>
-int KlassSymbolWriterImpl<Predicate>::package_symbols(PkgPtr pkg) {
- assert(pkg != NULL, "invariant");
- SymbolPtr pkg_name = pkg->name();
- assert(pkg_name != NULL, "invariant");
- SymbolEntryPtr package_symbol = this->_artifacts->map_symbol(pkg_name);
- assert(package_symbol != NULL, "invariant");
- return _unique_predicate(package_symbol->id()) ?
- write__artifact__symbol__entry__(this->_writer, package_symbol) : 0;
-}
-
-template <template <typename> class Predicate>
-int KlassSymbolWriterImpl<Predicate>::module_symbols(ModPtr module) {
- assert(module != NULL, "invariant");
- assert(module->is_named(), "invariant");
- int count = 0;
- SymbolPtr sym = module->name();
- SymbolEntryPtr entry = NULL;
- if (sym != NULL) {
- entry = this->_artifacts->map_symbol(sym);
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- }
- sym = module->version();
- if (sym != NULL) {
- entry = this->_artifacts->map_symbol(sym);
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- }
- sym = module->location();
- if (sym != NULL) {
- entry = this->_artifacts->map_symbol(sym);
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- }
- return count;
+int write__package__leakp(JfrCheckpointWriter* writer, const void* p) {
+ assert(p != NULL, "invariant");
+ PkgPtr pkg = (PkgPtr)p;
+ CLEAR_LEAKP(pkg);
+ return write_package(writer, pkg, true);
}
-template <template <typename> class Predicate>
-int KlassSymbolWriterImpl<Predicate>::class_loader_symbols(CldPtr cld) {
- assert(cld != NULL, "invariant");
- assert(!cld->is_unsafe_anonymous(), "invariant");
- int count = 0;
- // class loader type
- const Klass* class_loader_klass = cld->class_loader_klass();
- if (class_loader_klass == NULL) {
- // (primordial) boot class loader
- CStringEntryPtr entry = this->_artifacts->map_cstring(0);
- assert(entry != NULL, "invariant");
- assert(strncmp(entry->literal(),
- BOOTSTRAP_LOADER_NAME,
- BOOTSTRAP_LOADER_NAME_LEN) == 0, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__cstring__entry__(this->_writer, entry);
- }
- } else {
- const Symbol* class_loader_name = cld->name();
- if (class_loader_name != NULL) {
- SymbolEntryPtr entry = this->_artifacts->map_symbol(class_loader_name);
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- }
- }
- return count;
-}
-
-template <template <typename> class Predicate>
-int KlassSymbolWriterImpl<Predicate>::method_symbols(KlassPtr klass) {
- assert(_predicate(klass), "invariant");
- assert(_method_used_predicate(klass), "invariant");
- assert(METHOD_AND_CLASS_USED_ANY_EPOCH(klass), "invariant");
- int count = 0;
- const InstanceKlass* const ik = InstanceKlass::cast(klass);
- const int len = ik->methods()->length();
- for (int i = 0; i < len; ++i) {
- MethodPtr method = ik->methods()->at(i);
- if (_method_flag_predicate(method)) {
- SymbolEntryPtr entry = this->_artifacts->map_symbol(method->name());
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- entry = this->_artifacts->map_symbol(method->signature());
- assert(entry != NULL, "invariant");
- if (_unique_predicate(entry->id())) {
- count += write__artifact__symbol__entry__(this->_writer, entry);
- }
- }
- }
- return count;
+static void do_package(PackageEntry* entry) {
+ do_previous_epoch_artifact(_subsystem_callback, entry);
}
-typedef KlassSymbolWriterImpl<LeakPredicate> LeakKlassSymbolWriterImpl;
-typedef JfrArtifactWriterHost<LeakKlassSymbolWriterImpl, TYPE_SYMBOL> LeakKlassSymbolWriter;
-
-class ClearKlassAndMethods {
- private:
- ClearArtifact<KlassPtr> _clear_klass_tag_bits;
- ClearArtifact<MethodPtr> _clear_method_flag;
- MethodUsedPredicate<false> _method_used_predicate;
-
- public:
- ClearKlassAndMethods(bool class_unload) : _clear_klass_tag_bits(class_unload),
- _clear_method_flag(class_unload),
- _method_used_predicate(class_unload) {}
- bool operator()(KlassPtr klass) {
- if (_method_used_predicate(klass)) {
- const InstanceKlass* ik = InstanceKlass::cast(klass);
- const int len = ik->methods()->length();
- for (int i = 0; i < len; ++i) {
- MethodPtr method = ik->methods()->at(i);
- _clear_method_flag(method);
- }
- }
- _clear_klass_tag_bits(klass);
- return true;
- }
-};
-
-typedef CompositeFunctor<KlassPtr,
- TagLeakpKlassArtifact,
- LeakKlassWriter> LeakpKlassArtifactTagging;
-
-typedef CompositeFunctor<KlassPtr,
- LeakpKlassArtifactTagging,
- KlassWriter> CompositeKlassWriter;
-
-typedef CompositeFunctor<KlassPtr,
- CompositeKlassWriter,
- KlassArtifactRegistrator> CompositeKlassWriterRegistration;
-
-typedef CompositeFunctor<KlassPtr,
- KlassWriter,
- KlassArtifactRegistrator> KlassWriterRegistration;
-
-typedef JfrArtifactCallbackHost<KlassPtr, KlassWriterRegistration> KlassCallback;
-typedef JfrArtifactCallbackHost<KlassPtr, CompositeKlassWriterRegistration> CompositeKlassCallback;
-
-/*
- * Composite operation
- *
- * TagLeakpKlassArtifact ->
- * LeakpPredicate ->
- * LeakpKlassWriter ->
- * KlassPredicate ->
- * KlassWriter ->
- * KlassWriterRegistration
- */
-void JfrTypeSet::write_klass_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(!_artifacts->has_klass_entries(), "invariant");
- KlassArtifactRegistrator reg(_artifacts);
- KlassWriter kw(writer, _artifacts, _class_unload);
- KlassWriterRegistration kwr(&kw, ®);
- if (leakp_writer == NULL) {
- KlassCallback callback(&kwr);
- _subsystem_callback = &callback;
- do_klasses();
- return;
- }
- TagLeakpKlassArtifact tagging(_class_unload);
- LeakKlassWriter lkw(leakp_writer, _artifacts, _class_unload);
- LeakpKlassArtifactTagging lpkat(&tagging, &lkw);
- CompositeKlassWriter ckw(&lpkat, &kw);
- CompositeKlassWriterRegistration ckwr(&ckw, ®);
- CompositeKlassCallback callback(&ckwr);
- _subsystem_callback = &callback;
- do_klasses();
+static void do_packages() {
+ ClassLoaderDataGraph::packages_do(&do_package);
}
-typedef CompositeFunctor<PkgPtr,
- PackageWriter,
- ClearArtifact<PkgPtr> > PackageWriterWithClear;
-
-typedef CompositeFunctor<PkgPtr,
- LeakPackageWriter,
- PackageWriter> CompositePackageWriter;
-
-typedef CompositeFunctor<PkgPtr,
- CompositePackageWriter,
- ClearArtifact<PkgPtr> > CompositePackageWriterWithClear;
-
class PackageFieldSelector {
public:
typedef PkgPtr TypePtr;
@@ -583,60 +349,86 @@
}
};
-typedef KlassToFieldEnvelope<PackageFieldSelector,
- PackageWriterWithClear> KlassPackageWriterWithClear;
+typedef SerializePredicate<PkgPtr> PackagePredicate;
+typedef JfrPredicatedTypeWriterImplHost<PkgPtr, PackagePredicate, write__package> PackageWriterImpl;
+typedef JfrTypeWriterHost<PackageWriterImpl, TYPE_PACKAGE> PackageWriter;
+typedef CompositeFunctor<PkgPtr, PackageWriter, ClearArtifact<PkgPtr> > PackageWriterWithClear;
+typedef KlassToFieldEnvelope<PackageFieldSelector, PackageWriter> KlassPackageWriter;
+typedef JfrArtifactCallbackHost<PkgPtr, PackageWriterWithClear> PackageCallback;
-typedef KlassToFieldEnvelope<PackageFieldSelector,
- CompositePackageWriterWithClear> KlassCompositePackageWriterWithClear;
+typedef LeakPredicate<PkgPtr> LeakPackagePredicate;
+typedef JfrPredicatedTypeWriterImplHost<PkgPtr, LeakPackagePredicate, write__package__leakp> LeakPackageWriterImpl;
+typedef JfrTypeWriterHost<LeakPackageWriterImpl, TYPE_PACKAGE> LeakPackageWriter;
-typedef JfrArtifactCallbackHost<PkgPtr, PackageWriterWithClear> PackageCallback;
+typedef CompositeFunctor<PkgPtr, LeakPackageWriter, PackageWriter> CompositePackageWriter;
+typedef KlassToFieldEnvelope<PackageFieldSelector, CompositePackageWriter> KlassCompositePackageWriter;
+typedef KlassToFieldEnvelope<PackageFieldSelector, PackageWriterWithClear> KlassPackageWriterWithClear;
+typedef CompositeFunctor<PkgPtr, CompositePackageWriter, ClearArtifact<PkgPtr> > CompositePackageWriterWithClear;
typedef JfrArtifactCallbackHost<PkgPtr, CompositePackageWriterWithClear> CompositePackageCallback;
-/*
- * Composite operation
- *
- * LeakpPackageWriter ->
- * PackageWriter ->
- * ClearArtifact<PackageEntry>
- *
- */
-void JfrTypeSet::write_package_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<PkgPtr> clear(_class_unload);
- PackageWriter pw(writer, _artifacts, _class_unload);
- if (leakp_writer == NULL) {
+static void write_packages() {
+ assert(_writer != NULL, "invariant");
+ PackageWriter pw(_writer, _class_unload);
+ KlassPackageWriter kpw(&pw);
+ if (current_epoch()) {
+ _artifacts->iterate_klasses(kpw);
+ _artifacts->tally(pw);
+ return;
+ }
+ assert(previous_epoch(), "invariant");
+ if (_leakp_writer == NULL) {
+ _artifacts->iterate_klasses(kpw);
+ ClearArtifact<PkgPtr> clear;
PackageWriterWithClear pwwc(&pw, &clear);
- KlassPackageWriterWithClear kpwwc(&pwwc);
- _artifacts->iterate_klasses(kpwwc);
PackageCallback callback(&pwwc);
_subsystem_callback = &callback;
do_packages();
- return;
+ } else {
+ LeakPackageWriter lpw(_leakp_writer, _class_unload);
+ CompositePackageWriter cpw(&lpw, &pw);
+ KlassCompositePackageWriter kcpw(&cpw);
+ _artifacts->iterate_klasses(kcpw);
+ ClearArtifact<PkgPtr> clear;
+ CompositePackageWriterWithClear cpwwc(&cpw, &clear);
+ CompositePackageCallback callback(&cpwwc);
+ _subsystem_callback = &callback;
+ do_packages();
}
- LeakPackageWriter lpw(leakp_writer, _artifacts, _class_unload);
- CompositePackageWriter cpw(&lpw, &pw);
- CompositePackageWriterWithClear cpwwc(&cpw, &clear);
- KlassCompositePackageWriterWithClear ckpw(&cpwwc);
- _artifacts->iterate_klasses(ckpw);
- CompositePackageCallback callback(&cpwwc);
- _subsystem_callback = &callback;
- do_packages();
+ _artifacts->tally(pw);
}
-typedef CompositeFunctor<ModPtr,
- ModuleWriter,
- ClearArtifact<ModPtr> > ModuleWriterWithClear;
+static int write_module(JfrCheckpointWriter* writer, ModPtr mod, bool leakp) {
+ assert(mod != NULL, "invariant");
+ assert(_artifacts != NULL, "invariant");
+ writer->write(artifact_id(mod));
+ writer->write(mark_symbol(mod->name(), leakp));
+ writer->write(mark_symbol(mod->version(), leakp));
+ writer->write(mark_symbol(mod->location(), leakp));
+ writer->write(cld_id(mod->loader_data(), leakp));
+ return 1;
+}
-typedef CompositeFunctor<ModPtr,
- LeakModuleWriter,
- ModuleWriter> CompositeModuleWriter;
+int write__module(JfrCheckpointWriter* writer, const void* m) {
+ assert(m != NULL, "invariant");
+ ModPtr mod = (ModPtr)m;
+ set_serialized(mod);
+ return write_module(writer, mod, false);
+}
-typedef CompositeFunctor<ModPtr,
- CompositeModuleWriter,
- ClearArtifact<ModPtr> > CompositeModuleWriterWithClear;
+int write__module__leakp(JfrCheckpointWriter* writer, const void* m) {
+ assert(m != NULL, "invariant");
+ ModPtr mod = (ModPtr)m;
+ CLEAR_LEAKP(mod);
+ return write_module(writer, mod, true);
+}
-typedef JfrArtifactCallbackHost<ModPtr, ModuleWriterWithClear> ModuleCallback;
-typedef JfrArtifactCallbackHost<ModPtr, CompositeModuleWriterWithClear> CompositeModuleCallback;
+static void do_module(ModuleEntry* entry) {
+ do_previous_epoch_artifact(_subsystem_callback, entry);
+}
+
+static void do_modules() {
+ ClassLoaderDataGraph::modules_do(&do_module);
+}
class ModuleFieldSelector {
public:
@@ -648,47 +440,88 @@
}
};
-typedef KlassToFieldEnvelope<ModuleFieldSelector,
- ModuleWriterWithClear> KlassModuleWriterWithClear;
+typedef SerializePredicate<ModPtr> ModulePredicate;
+typedef JfrPredicatedTypeWriterImplHost<ModPtr, ModulePredicate, write__module> ModuleWriterImpl;
+typedef JfrTypeWriterHost<ModuleWriterImpl, TYPE_MODULE> ModuleWriter;
+typedef CompositeFunctor<ModPtr, ModuleWriter, ClearArtifact<ModPtr> > ModuleWriterWithClear;
+typedef JfrArtifactCallbackHost<ModPtr, ModuleWriterWithClear> ModuleCallback;
+typedef KlassToFieldEnvelope<ModuleFieldSelector, ModuleWriter> KlassModuleWriter;
-typedef KlassToFieldEnvelope<ModuleFieldSelector,
- CompositeModuleWriterWithClear> KlassCompositeModuleWriterWithClear;
+typedef LeakPredicate<ModPtr> LeakModulePredicate;
+typedef JfrPredicatedTypeWriterImplHost<ModPtr, LeakModulePredicate, write__module__leakp> LeakModuleWriterImpl;
+typedef JfrTypeWriterHost<LeakModuleWriterImpl, TYPE_MODULE> LeakModuleWriter;
-/*
- * Composite operation
- *
- * LeakpModuleWriter ->
- * ModuleWriter ->
- * ClearArtifact<ModuleEntry>
- */
-void JfrTypeSet::write_module_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<ModPtr> clear(_class_unload);
- ModuleWriter mw(writer, _artifacts, _class_unload);
- if (leakp_writer == NULL) {
+typedef CompositeFunctor<ModPtr, LeakModuleWriter, ModuleWriter> CompositeModuleWriter;
+typedef KlassToFieldEnvelope<ModuleFieldSelector, CompositeModuleWriter> KlassCompositeModuleWriter;
+typedef CompositeFunctor<ModPtr, CompositeModuleWriter, ClearArtifact<ModPtr> > CompositeModuleWriterWithClear;
+typedef JfrArtifactCallbackHost<ModPtr, CompositeModuleWriterWithClear> CompositeModuleCallback;
+
+static void write_modules() {
+ assert(_writer != NULL, "invariant");
+ ModuleWriter mw(_writer, _class_unload);
+ KlassModuleWriter kmw(&mw);
+ if (current_epoch()) {
+ _artifacts->iterate_klasses(kmw);
+ _artifacts->tally(mw);
+ return;
+ }
+ assert(previous_epoch(), "invariant");
+ if (_leakp_writer == NULL) {
+ _artifacts->iterate_klasses(kmw);
+ ClearArtifact<ModPtr> clear;
ModuleWriterWithClear mwwc(&mw, &clear);
- KlassModuleWriterWithClear kmwwc(&mwwc);
- _artifacts->iterate_klasses(kmwwc);
ModuleCallback callback(&mwwc);
_subsystem_callback = &callback;
do_modules();
- return;
+ } else {
+ LeakModuleWriter lmw(_leakp_writer, _class_unload);
+ CompositeModuleWriter cmw(&lmw, &mw);
+ KlassCompositeModuleWriter kcpw(&cmw);
+ _artifacts->iterate_klasses(kcpw);
+ ClearArtifact<ModPtr> clear;
+ CompositeModuleWriterWithClear cmwwc(&cmw, &clear);
+ CompositeModuleCallback callback(&cmwwc);
+ _subsystem_callback = &callback;
+ do_modules();
}
- LeakModuleWriter lmw(leakp_writer, _artifacts, _class_unload);
- CompositeModuleWriter cmw(&lmw, &mw);
- CompositeModuleWriterWithClear cmwwc(&cmw, &clear);
- KlassCompositeModuleWriterWithClear kmwwc(&cmwwc);
- _artifacts->iterate_klasses(kmwwc);
- CompositeModuleCallback callback(&cmwwc);
- _subsystem_callback = &callback;
- do_modules();
+ _artifacts->tally(mw);
}
-typedef CompositeFunctor<CldPtr, CldWriter, ClearArtifact<CldPtr> > CldWriterWithClear;
-typedef CompositeFunctor<CldPtr, LeakCldWriter, CldWriter> CompositeCldWriter;
-typedef CompositeFunctor<CldPtr, CompositeCldWriter, ClearArtifact<CldPtr> > CompositeCldWriterWithClear;
-typedef JfrArtifactCallbackHost<CldPtr, CldWriterWithClear> CldCallback;
-typedef JfrArtifactCallbackHost<CldPtr, CompositeCldWriterWithClear> CompositeCldCallback;
+static int write_classloader(JfrCheckpointWriter* writer, CldPtr cld, bool leakp) {
+ assert(cld != NULL, "invariant");
+ assert(!cld->is_unsafe_anonymous(), "invariant");
+ // class loader type
+ const Klass* class_loader_klass = cld->class_loader_klass();
+ if (class_loader_klass == NULL) {
+ // (primordial) boot class loader
+ writer->write(artifact_id(cld)); // class loader instance id
+ writer->write((traceid)0); // class loader type id (absence of)
+ writer->write(get_bootstrap_name(leakp)); // maps to synthetic name -> "bootstrap"
+ } else {
+ writer->write(artifact_id(cld)); // class loader instance id
+ writer->write(artifact_id(class_loader_klass)); // class loader type id
+ writer->write(mark_symbol(cld->name(), leakp)); // class loader instance name
+ }
+ return 1;
+}
+
+int write__classloader(JfrCheckpointWriter* writer, const void* c) {
+ assert(c != NULL, "invariant");
+ CldPtr cld = (CldPtr)c;
+ set_serialized(cld);
+ return write_classloader(writer, cld, false);
+}
+
+int write__classloader__leakp(JfrCheckpointWriter* writer, const void* c) {
+ assert(c != NULL, "invariant");
+ CldPtr cld = (CldPtr)c;
+ CLEAR_LEAKP(cld);
+ return write_classloader(writer, cld, true);
+}
+
+static void do_class_loader_data(ClassLoaderData* cld) {
+ do_previous_epoch_artifact(_subsystem_callback, cld);
+}
class CldFieldSelector {
public:
@@ -700,289 +533,328 @@
}
};
-typedef KlassToFieldEnvelope<CldFieldSelector, CldWriterWithClear> KlassCldWriterWithClear;
-typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriterWithClear> KlassCompositeCldWriterWithClear;
+class CLDCallback : public CLDClosure {
+ public:
+ CLDCallback() {}
+ void do_cld(ClassLoaderData* cld) {
+ assert(cld != NULL, "invariant");
+ if (cld->is_unsafe_anonymous()) {
+ return;
+ }
+ do_class_loader_data(cld);
+ }
+};
+
+static void do_class_loaders() {
+ CLDCallback cld_cb;
+ ClassLoaderDataGraph::loaded_cld_do(&cld_cb);
+}
+
+typedef SerializePredicate<CldPtr> CldPredicate;
+typedef JfrPredicatedTypeWriterImplHost<CldPtr, CldPredicate, write__classloader> CldWriterImpl;
+typedef JfrTypeWriterHost<CldWriterImpl, TYPE_CLASSLOADER> CldWriter;
+typedef CompositeFunctor<CldPtr, CldWriter, ClearArtifact<CldPtr> > CldWriterWithClear;
+typedef JfrArtifactCallbackHost<CldPtr, CldWriterWithClear> CldCallback;
+typedef KlassToFieldEnvelope<CldFieldSelector, CldWriter> KlassCldWriter;
-/*
- * Composite operation
- *
- * LeakpClassLoaderWriter ->
- * ClassLoaderWriter ->
- * ClearArtifact<ClassLoaderData>
- */
-void JfrTypeSet::write_class_loader_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<CldPtr> clear(_class_unload);
- CldWriter cldw(writer, _artifacts, _class_unload);
- if (leakp_writer == NULL) {
+typedef LeakPredicate<CldPtr> LeakCldPredicate;
+typedef JfrPredicatedTypeWriterImplHost<CldPtr, LeakCldPredicate, write__classloader__leakp> LeakCldWriterImpl;
+typedef JfrTypeWriterHost<LeakCldWriterImpl, TYPE_CLASSLOADER> LeakCldWriter;
+
+typedef CompositeFunctor<CldPtr, LeakCldWriter, CldWriter> CompositeCldWriter;
+typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriter> KlassCompositeCldWriter;
+typedef CompositeFunctor<CldPtr, CompositeCldWriter, ClearArtifact<CldPtr> > CompositeCldWriterWithClear;
+typedef JfrArtifactCallbackHost<CldPtr, CompositeCldWriterWithClear> CompositeCldCallback;
+
+static void write_classloaders() {
+ assert(_writer != NULL, "invariant");
+ CldWriter cldw(_writer, _class_unload);
+ KlassCldWriter kcw(&cldw);
+ if (current_epoch()) {
+ _artifacts->iterate_klasses(kcw);
+ _artifacts->tally(cldw);
+ return;
+ }
+ assert(previous_epoch(), "invariant");
+ if (_leakp_writer == NULL) {
+ _artifacts->iterate_klasses(kcw);
+ ClearArtifact<CldPtr> clear;
CldWriterWithClear cldwwc(&cldw, &clear);
- KlassCldWriterWithClear kcldwwc(&cldwwc);
- _artifacts->iterate_klasses(kcldwwc);
CldCallback callback(&cldwwc);
_subsystem_callback = &callback;
do_class_loaders();
- return;
+ } else {
+ LeakCldWriter lcldw(_leakp_writer, _class_unload);
+ CompositeCldWriter ccldw(&lcldw, &cldw);
+ KlassCompositeCldWriter kccldw(&ccldw);
+ _artifacts->iterate_klasses(kccldw);
+ ClearArtifact<CldPtr> clear;
+ CompositeCldWriterWithClear ccldwwc(&ccldw, &clear);
+ CompositeCldCallback callback(&ccldwwc);
+ _subsystem_callback = &callback;
+ do_class_loaders();
}
- LeakCldWriter lcldw(leakp_writer, _artifacts, _class_unload);
- CompositeCldWriter ccldw(&lcldw, &cldw);
- CompositeCldWriterWithClear ccldwwc(&ccldw, &clear);
- KlassCompositeCldWriterWithClear kcclwwc(&ccldwwc);
- _artifacts->iterate_klasses(kcclwwc);
- CompositeCldCallback callback(&ccldwwc);
- _subsystem_callback = &callback;
- do_class_loaders();
+ _artifacts->tally(cldw);
+}
+
+static u1 get_visibility(MethodPtr method) {
+ assert(method != NULL, "invariant");
+ return const_cast<Method*>(method)->is_hidden() ? (u1)1 : (u1)0;
+}
+
+template <>
+void set_serialized<Method>(MethodPtr method) {
+ assert(method != NULL, "invariant");
+ SET_METHOD_SERIALIZED(method);
+ assert(IS_METHOD_SERIALIZED(method), "invariant");
}
-template <bool predicate_bool, typename MethodFunctor>
+static int write_method(JfrCheckpointWriter* writer, MethodPtr method, bool leakp) {
+ assert(writer != NULL, "invariant");
+ assert(method != NULL, "invariant");
+ assert(_artifacts != NULL, "invariant");
+ KlassPtr klass = method->method_holder();
+ assert(klass != NULL, "invariant");
+ writer->write(method_id(klass, method));
+ writer->write(artifact_id(klass));
+ writer->write(mark_symbol(method->name(), leakp));
+ writer->write(mark_symbol(method->signature(), leakp));
+ writer->write((u2)get_flags(method));
+ writer->write(get_visibility(method));
+ return 1;
+}
+
+int write__method(JfrCheckpointWriter* writer, const void* m) {
+ assert(m != NULL, "invariant");
+ MethodPtr method = (MethodPtr)m;
+ set_serialized(method);
+ return write_method(writer, method, false);
+}
+
+int write__method__leakp(JfrCheckpointWriter* writer, const void* m) {
+ assert(m != NULL, "invariant");
+ MethodPtr method = (MethodPtr)m;
+ return write_method(writer, method, true);
+}
+
+template <typename MethodCallback, typename KlassCallback, bool leakp>
class MethodIteratorHost {
private:
- MethodFunctor _method_functor;
- MethodUsedPredicate<predicate_bool> _method_used_predicate;
- MethodFlagPredicate _method_flag_predicate;
-
+ MethodCallback _method_cb;
+ KlassCallback _klass_cb;
+ MethodUsedPredicate<leakp> _method_used_predicate;
+ MethodFlagPredicate<leakp> _method_flag_predicate;
public:
MethodIteratorHost(JfrCheckpointWriter* writer,
- JfrArtifactSet* artifacts,
- bool class_unload,
+ bool current_epoch = false,
+ bool class_unload = false,
bool skip_header = false) :
- _method_functor(writer, artifacts, class_unload, skip_header),
- _method_used_predicate(class_unload),
- _method_flag_predicate(class_unload) {}
+ _method_cb(writer, class_unload, skip_header),
+ _klass_cb(writer, class_unload, skip_header),
+ _method_used_predicate(current_epoch),
+ _method_flag_predicate(current_epoch) {}
bool operator()(KlassPtr klass) {
if (_method_used_predicate(klass)) {
- assert(METHOD_AND_CLASS_USED_ANY_EPOCH(klass), "invariant");
- const InstanceKlass* ik = InstanceKlass::cast(klass);
+ const InstanceKlass* const ik = InstanceKlass::cast(klass);
const int len = ik->methods()->length();
for (int i = 0; i < len; ++i) {
MethodPtr method = ik->methods()->at(i);
if (_method_flag_predicate(method)) {
- _method_functor(method);
+ _method_cb(method);
}
}
}
- return true;
+ return _klass_cb(klass);
}
- int count() const { return _method_functor.count(); }
- void add(int count) { _method_functor.add(count); }
+ int count() const { return _method_cb.count(); }
+ void add(int count) { _method_cb.add(count); }
};
-typedef MethodIteratorHost<true /*leakp */, MethodWriterImpl> LeakMethodWriter;
-typedef MethodIteratorHost<false, MethodWriterImpl> MethodWriter;
-typedef CompositeFunctor<KlassPtr, LeakMethodWriter, MethodWriter> CompositeMethodWriter;
-
-/*
- * Composite operation
- *
- * LeakpMethodWriter ->
- * MethodWriter
- */
-void JfrTypeSet::write_method_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(_artifacts->has_klass_entries(), "invariant");
- MethodWriter mw(writer, _artifacts, _class_unload);
- if (leakp_writer == NULL) {
- _artifacts->iterate_klasses(mw);
- return;
- }
- LeakMethodWriter lpmw(leakp_writer, _artifacts, _class_unload);
- CompositeMethodWriter cmw(&lpmw, &mw);
- _artifacts->iterate_klasses(cmw);
-}
-static void write_symbols_leakp(JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool class_unload) {
- assert(leakp_writer != NULL, "invariant");
- assert(artifacts != NULL, "invariant");
- LeakKlassSymbolWriter lpksw(leakp_writer, artifacts, class_unload);
- artifacts->iterate_klasses(lpksw);
-}
-static void write_symbols(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool class_unload) {
- assert(writer != NULL, "invariant");
- assert(artifacts != NULL, "invariant");
- if (leakp_writer != NULL) {
- write_symbols_leakp(leakp_writer, artifacts, class_unload);
- }
- // iterate all registered symbols
- SymbolEntryWriter symbol_writer(writer, artifacts, class_unload);
- artifacts->iterate_symbols(symbol_writer);
- CStringEntryWriter cstring_writer(writer, artifacts, class_unload, true); // skip header
- artifacts->iterate_cstrings(cstring_writer);
- symbol_writer.add(cstring_writer.count());
-}
-
-bool JfrTypeSet::_class_unload = false;
-JfrArtifactSet* JfrTypeSet::_artifacts = NULL;
-JfrArtifactClosure* JfrTypeSet::_subsystem_callback = NULL;
-
-void JfrTypeSet::write_symbol_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
- assert(writer != NULL, "invariant");
- assert(_artifacts->has_klass_entries(), "invariant");
- write_symbols(writer, leakp_writer, _artifacts, _class_unload);
-}
-
-void JfrTypeSet::do_unloaded_klass(Klass* klass) {
- assert(klass != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (IS_JDK_JFR_EVENT_SUBKLASS(klass)) {
- JfrEventClasses::increment_unloaded_event_class();
- }
- if (USED_THIS_EPOCH(klass)) { // includes leakp subset
- _subsystem_callback->do_artifact(klass);
- return;
- }
- if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
- SET_LEAKP_USED_THIS_EPOCH(klass); // tag leakp "safe byte" for subset inclusion
- _subsystem_callback->do_artifact(klass);
- }
-}
-
-void JfrTypeSet::do_klass(Klass* klass) {
- assert(klass != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (USED_PREV_EPOCH(klass)) { // includes leakp subset
- _subsystem_callback->do_artifact(klass);
- return;
- }
- if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
- SET_LEAKP_USED_PREV_EPOCH(klass); // tag leakp "safe byte" for subset inclusion
- _subsystem_callback->do_artifact(klass);
- }
-}
-
-void JfrTypeSet::do_klasses() {
- if (_class_unload) {
- ClassLoaderDataGraph::classes_unloading_do(&do_unloaded_klass);
- return;
- }
- ClassLoaderDataGraph::classes_do(&do_klass);
-}
-
-void JfrTypeSet::do_unloaded_package(PackageEntry* entry) {
- assert(entry != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
-}
-
-void JfrTypeSet::do_package(PackageEntry* entry) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
-}
-
-void JfrTypeSet::do_packages() {
- if (_class_unload) {
- ClassLoaderDataGraph::packages_unloading_do(&do_unloaded_package);
- return;
- }
- ClassLoaderDataGraph::packages_do(&do_package);
-}
-void JfrTypeSet::do_unloaded_module(ModuleEntry* entry) {
- assert(entry != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
-}
-
-void JfrTypeSet::do_module(ModuleEntry* entry) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
-}
-
-void JfrTypeSet::do_modules() {
- if (_class_unload) {
- ClassLoaderDataGraph::modules_unloading_do(&do_unloaded_module);
- return;
- }
- ClassLoaderDataGraph::modules_do(&do_module);
-}
-
-void JfrTypeSet::do_unloaded_class_loader_data(ClassLoaderData* cld) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(cld)) { // includes leakp subset
- _subsystem_callback->do_artifact(cld);
- }
-}
-
-void JfrTypeSet::do_class_loader_data(ClassLoaderData* cld) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(cld)) { // includes leakp subset
- _subsystem_callback->do_artifact(cld);
- }
-}
-
-class CLDCallback : public CLDClosure {
- private:
- bool _class_unload;
+template <typename T, template <typename> class Impl>
+class Wrapper {
+ Impl<T> _t;
public:
- CLDCallback(bool class_unload) : _class_unload(class_unload) {}
- void do_cld(ClassLoaderData* cld) {
- assert(cld != NULL, "invariant");
- if (cld->is_unsafe_anonymous()) {
- return;
- }
- if (_class_unload) {
- JfrTypeSet::do_unloaded_class_loader_data(cld);
- return;
- }
- JfrTypeSet::do_class_loader_data(cld);
+ Wrapper(JfrCheckpointWriter*, bool, bool) : _t() {}
+ bool operator()(T const& value) {
+ return _t(value);
}
};
-void JfrTypeSet::do_class_loaders() {
- CLDCallback cld_cb(_class_unload);
- if (_class_unload) {
- ClassLoaderDataGraph::cld_unloading_do(&cld_cb);
- return;
+typedef SerializePredicate<MethodPtr> MethodPredicate;
+typedef JfrPredicatedTypeWriterImplHost<MethodPtr, MethodPredicate, write__method> MethodWriterImplTarget;
+typedef Wrapper<KlassPtr, Stub> KlassCallbackStub;
+typedef JfrTypeWriterHost<MethodWriterImplTarget, TYPE_METHOD> MethodWriterImpl;
+typedef MethodIteratorHost<MethodWriterImpl, KlassCallbackStub, false> MethodWriter;
+
+typedef LeakPredicate<MethodPtr> LeakMethodPredicate;
+typedef JfrPredicatedTypeWriterImplHost<MethodPtr, LeakMethodPredicate, write__method__leakp> LeakMethodWriterImplTarget;
+typedef JfrTypeWriterHost<LeakMethodWriterImplTarget, TYPE_METHOD> LeakMethodWriterImpl;
+typedef MethodIteratorHost<LeakMethodWriterImpl, KlassCallbackStub, true> LeakMethodWriter;
+typedef CompositeFunctor<KlassPtr, LeakMethodWriter, MethodWriter> CompositeMethodWriter;
+
+static void write_methods() {
+ assert(_writer != NULL, "invariant");
+ MethodWriter mw(_writer, current_epoch(), _class_unload);
+ if (_leakp_writer == NULL) {
+ _artifacts->iterate_klasses(mw);
+ } else {
+ LeakMethodWriter lpmw(_leakp_writer, current_epoch(), _class_unload);
+ CompositeMethodWriter cmw(&lpmw, &mw);
+ _artifacts->iterate_klasses(cmw);
}
- ClassLoaderDataGraph::loaded_cld_do(&cld_cb);
+ _artifacts->tally(mw);
+}
+
+template <>
+void set_serialized<JfrSymbolId::SymbolEntry>(SymbolEntryPtr ptr) {
+ assert(ptr != NULL, "invariant");
+ ptr->set_serialized();
+ assert(ptr->is_serialized(), "invariant");
+}
+
+template <>
+void set_serialized<JfrSymbolId::CStringEntry>(CStringEntryPtr ptr) {
+ assert(ptr != NULL, "invariant");
+ ptr->set_serialized();
+ assert(ptr->is_serialized(), "invariant");
+}
+
+static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool leakp) {
+ assert(writer != NULL, "invariant");
+ assert(entry != NULL, "invariant");
+ ResourceMark rm;
+ writer->write(create_symbol_id(entry->id()));
+ writer->write(entry->value()->as_C_string());
+ return 1;
+}
+
+int write__symbol(JfrCheckpointWriter* writer, const void* e) {
+ assert(e != NULL, "invariant");
+ SymbolEntryPtr entry = (SymbolEntryPtr)e;
+ set_serialized(entry);
+ return write_symbol(writer, entry, false);
+}
+
+int write__symbol__leakp(JfrCheckpointWriter* writer, const void* e) {
+ assert(e != NULL, "invariant");
+ SymbolEntryPtr entry = (SymbolEntryPtr)e;
+ return write_symbol(writer, entry, true);
+}
+
+static int write_cstring(JfrCheckpointWriter* writer, CStringEntryPtr entry, bool leakp) {
+ assert(writer != NULL, "invariant");
+ assert(entry != NULL, "invariant");
+ writer->write(create_symbol_id(entry->id()));
+ writer->write(entry->value());
+ return 1;
+}
+
+int write__cstring(JfrCheckpointWriter* writer, const void* e) {
+ assert(e != NULL, "invariant");
+ CStringEntryPtr entry = (CStringEntryPtr)e;
+ set_serialized(entry);
+ return write_cstring(writer, entry, false);
}
-static void clear_artifacts(JfrArtifactSet* artifacts,
- bool class_unload) {
- assert(artifacts != NULL, "invariant");
- assert(artifacts->has_klass_entries(), "invariant");
+int write__cstring__leakp(JfrCheckpointWriter* writer, const void* e) {
+ assert(e != NULL, "invariant");
+ CStringEntryPtr entry = (CStringEntryPtr)e;
+ return write_cstring(writer, entry, true);
+}
+
+typedef SymbolPredicate<SymbolEntryPtr, false> SymPredicate;
+typedef JfrPredicatedTypeWriterImplHost<SymbolEntryPtr, SymPredicate, write__symbol> SymbolEntryWriterImpl;
+typedef JfrTypeWriterHost<SymbolEntryWriterImpl, TYPE_SYMBOL> SymbolEntryWriter;
+typedef SymbolPredicate<CStringEntryPtr, false> CStringPredicate;
+typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, CStringPredicate, write__cstring> CStringEntryWriterImpl;
+typedef JfrTypeWriterHost<CStringEntryWriterImpl, TYPE_SYMBOL> CStringEntryWriter;
+
+typedef SymbolPredicate<SymbolEntryPtr, true> LeakSymPredicate;
+typedef JfrPredicatedTypeWriterImplHost<SymbolEntryPtr, LeakSymPredicate, write__symbol__leakp> LeakSymbolEntryWriterImpl;
+typedef JfrTypeWriterHost<LeakSymbolEntryWriterImpl, TYPE_SYMBOL> LeakSymbolEntryWriter;
+typedef CompositeFunctor<SymbolEntryPtr, LeakSymbolEntryWriter, SymbolEntryWriter> CompositeSymbolWriter;
+typedef SymbolPredicate<CStringEntryPtr, true> LeakCStringPredicate;
+typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, LeakCStringPredicate, write__cstring__leakp> LeakCStringEntryWriterImpl;
+typedef JfrTypeWriterHost<LeakCStringEntryWriterImpl, TYPE_SYMBOL> LeakCStringEntryWriter;
+typedef CompositeFunctor<CStringEntryPtr, LeakCStringEntryWriter, CStringEntryWriter> CompositeCStringWriter;
+
+static void write_symbols_with_leakp() {
+ assert(_leakp_writer != NULL, "invariant");
+ SymbolEntryWriter sw(_writer, _class_unload);
+ LeakSymbolEntryWriter lsw(_leakp_writer, _class_unload);
+ CompositeSymbolWriter csw(&lsw, &sw);
+ _artifacts->iterate_symbols(csw);
+ CStringEntryWriter ccsw(_writer, _class_unload, true); // skip header
+ LeakCStringEntryWriter lccsw(_leakp_writer, _class_unload, true); // skip header
+ CompositeCStringWriter cccsw(&lccsw, &ccsw);
+ _artifacts->iterate_cstrings(cccsw);
+ sw.add(ccsw.count());
+ lsw.add(lccsw.count());
+ _artifacts->tally(sw);
+}
- // untag
- ClearKlassAndMethods clear(class_unload);
- artifacts->iterate_klasses(clear);
- artifacts->clear();
+static void write_symbols() {
+ assert(_writer != NULL, "invariant");
+ if (_leakp_writer != NULL) {
+ write_symbols_with_leakp();
+ return;
+ }
+ SymbolEntryWriter sw(_writer, _class_unload);
+ _artifacts->iterate_symbols(sw);
+ CStringEntryWriter csw(_writer, _class_unload, true); // skip header
+ _artifacts->iterate_cstrings(csw);
+ sw.add(csw.count());
+ _artifacts->tally(sw);
+}
+
+typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
+typedef Wrapper<MethodPtr, ClearArtifact> ClearMethodFlag;
+typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, false> ClearKlassAndMethods;
+
+static size_t teardown() {
+ assert(_artifacts != NULL, "invariant");
+ const size_t total_count = _artifacts->total_count();
+ if (previous_epoch()) {
+ assert(_writer != NULL, "invariant");
+ ClearKlassAndMethods clear(_writer);
+ _artifacts->iterate_klasses(clear);
+ _artifacts->clear();
+ ++checkpoint_id;
+ }
+ return total_count;
+}
+
+static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
+ _writer = writer;
+ _leakp_writer = leakp_writer;
+ _class_unload = class_unload;
+ if (_artifacts == NULL) {
+ _artifacts = new JfrArtifactSet(class_unload);
+ } else {
+ _artifacts->initialize(class_unload);
+ }
+ assert(_artifacts != NULL, "invariant");
+ assert(!_artifacts->has_klass_entries(), "invariant");
}
/**
* Write all "tagged" (in-use) constant artifacts and their dependencies.
*/
-void JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
+size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
assert(writer != NULL, "invariant");
ResourceMark rm;
- // initialization begin
- _class_unload = class_unload;
- ++checkpoint_id;
- if (_artifacts == NULL) {
- _artifacts = new JfrArtifactSet(class_unload);
- _subsystem_callback = NULL;
- } else {
- _artifacts->initialize(class_unload);
- _subsystem_callback = NULL;
- }
- assert(_artifacts != NULL, "invariant");
- assert(!_artifacts->has_klass_entries(), "invariant");
- assert(_subsystem_callback == NULL, "invariant");
- // initialization complete
-
+ setup(writer, leakp_writer, class_unload);
// write order is important because an individual write step
// might tag an artifact to be written in a subsequent step
- write_klass_constants(writer, leakp_writer);
- if (_artifacts->has_klass_entries()) {
- write_package_constants(writer, leakp_writer);
- write_module_constants(writer, leakp_writer);
- write_class_loader_constants(writer, leakp_writer);
- write_method_constants(writer, leakp_writer);
- write_symbol_constants(writer, leakp_writer);
- clear_artifacts(_artifacts, class_unload);
+ if (!write_klasses()) {
+ return 0;
}
+ write_packages();
+ write_modules();
+ write_classloaders();
+ write_methods();
+ write_symbols();
+ return teardown();
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -27,47 +27,11 @@
#include "jfr/utilities/jfrAllocation.hpp"
-class ClassLoaderData;
-class JfrArtifactClosure;
-class JfrArtifactSet;
class JfrCheckpointWriter;
-class Klass;
-
-class ModuleEntry;
-class PackageEntry;
class JfrTypeSet : AllStatic {
- friend class CLDCallback;
- friend class JfrTypeManager;
- friend class TypeSetSerialization;
- private:
- static JfrArtifactSet* _artifacts;
- static JfrArtifactClosure* _subsystem_callback;
- static bool _class_unload;
-
- static void do_klass(Klass* k);
- static void do_unloaded_klass(Klass* k);
- static void do_klasses();
-
- static void do_package(PackageEntry* entry);
- static void do_unloaded_package(PackageEntry* entry);
- static void do_packages();
-
- static void do_module(ModuleEntry* entry);
- static void do_unloaded_module(ModuleEntry* entry);
- static void do_modules();
-
- static void do_class_loader_data(ClassLoaderData* cld);
- static void do_unloaded_class_loader_data(ClassLoaderData* cld);
- static void do_class_loaders();
-
- static void write_klass_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void write_package_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void write_module_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void write_class_loader_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void write_method_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void write_symbol_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
+ public:
+ static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -28,15 +28,28 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
-JfrSymbolId::JfrSymbolId() : _sym_table(new SymbolTable(this)), _cstring_table(new CStringTable(this)), _symbol_id_counter(0) {
+static JfrSymbolId::CStringEntry* bootstrap = NULL;
+
+JfrSymbolId::JfrSymbolId() :
+ _sym_table(new SymbolTable(this)),
+ _cstring_table(new CStringTable(this)),
+ _sym_list(NULL),
+ _cstring_list(NULL),
+ _symbol_id_counter(1),
+ _class_unload(false) {
assert(_sym_table != NULL, "invariant");
assert(_cstring_table != NULL, "invariant");
- initialize();
+ bootstrap = new CStringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME);
+ assert(bootstrap != NULL, "invariant");
+ bootstrap->set_id(1);
+ _cstring_list = bootstrap;
}
-void JfrSymbolId::initialize() {
+JfrSymbolId::~JfrSymbolId() {
clear();
- assert(_symbol_id_counter == 0, "invariant");
+ delete _sym_table;
+ delete _cstring_table;
+ delete bootstrap;
}
void JfrSymbolId::clear() {
@@ -51,106 +64,115 @@
_cstring_table->clear_entries();
}
assert(!_cstring_table->has_entries(), "invariant");
- _symbol_id_counter = 0;
-}
-JfrSymbolId::~JfrSymbolId() {
- delete _sym_table;
- delete _cstring_table;
+ _sym_list = NULL;
+ _cstring_list = NULL;
+ _symbol_id_counter = 1;
+
+ assert(bootstrap != NULL, "invariant");
+ bootstrap->reset();
+ _cstring_list = bootstrap;
}
-traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const Klass* k) {
- assert(k != NULL, "invariant");
- assert(k->is_instance_klass(), "invariant");
- assert(is_unsafe_anonymous_klass(k), "invariant");
-
- uintptr_t anonymous_symbol_hash_code = 0;
- const char* const anonymous_symbol =
- create_unsafe_anonymous_klass_symbol((const InstanceKlass*)k, anonymous_symbol_hash_code);
-
- if (anonymous_symbol == NULL) {
- return 0;
- }
-
- assert(anonymous_symbol_hash_code != 0, "invariant");
- traceid symbol_id = mark(anonymous_symbol, anonymous_symbol_hash_code);
- assert(mark(anonymous_symbol, anonymous_symbol_hash_code) == symbol_id, "invariant");
- return symbol_id;
+void JfrSymbolId::set_class_unload(bool class_unload) {
+ _class_unload = class_unload;
}
-const JfrSymbolId::SymbolEntry* JfrSymbolId::map_symbol(const Symbol* symbol) const {
- return _sym_table->lookup_only(symbol, (uintptr_t)const_cast<Symbol*>(symbol)->identity_hash());
-}
-
-const JfrSymbolId::SymbolEntry* JfrSymbolId::map_symbol(uintptr_t hash) const {
- return _sym_table->lookup_only(NULL, hash);
+void JfrSymbolId::on_link(const SymbolEntry* entry) {
+ assert(entry != NULL, "invariant");
+ const_cast<Symbol*>(entry->literal())->increment_refcount();
+ assert(entry->id() == 0, "invariant");
+ entry->set_id(++_symbol_id_counter);
+ entry->set_list_next(_sym_list);
+ _sym_list = entry;
}
-const JfrSymbolId::CStringEntry* JfrSymbolId::map_cstring(uintptr_t hash) const {
- return _cstring_table->lookup_only(NULL, hash);
-}
-
-void JfrSymbolId::assign_id(SymbolEntry* entry) {
- assert(entry != NULL, "invariant");
- assert(entry->id() == 0, "invariant");
- entry->set_id(++_symbol_id_counter);
-}
-
-bool JfrSymbolId::equals(const Symbol* query, uintptr_t hash, const SymbolEntry* entry) {
+bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) {
// query might be NULL
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
return true;
}
-void JfrSymbolId::assign_id(CStringEntry* entry) {
+void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
+ assert(entry != NULL, "invariant");
+ const_cast<Symbol*>(entry->literal())->decrement_refcount();
+}
+
+void JfrSymbolId::on_link(const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_symbol_id_counter);
+ entry->set_list_next(_cstring_list);
+ _cstring_list = entry;
}
-bool JfrSymbolId::equals(const char* query, uintptr_t hash, const CStringEntry* entry) {
- // query might be NULL
+bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
return true;
}
-traceid JfrSymbolId::mark(const Klass* k) {
- assert(k != NULL, "invariant");
- traceid symbol_id = 0;
- if (is_unsafe_anonymous_klass(k)) {
- symbol_id = mark_unsafe_anonymous_klass_name(k);
+void JfrSymbolId::on_unlink(const CStringEntry* entry) {
+ assert(entry != NULL, "invariant");
+ JfrCHeapObj::free(const_cast<char*>(entry->literal()), strlen(entry->literal() + 1));
+}
+
+traceid JfrSymbolId::bootstrap_name(bool leakp) {
+ assert(bootstrap != NULL, "invariant");
+ if (leakp) {
+ bootstrap->set_leakp();
}
- if (0 == symbol_id) {
- const Symbol* const sym = k->name();
- if (sym != NULL) {
- symbol_id = mark(sym);
- }
- }
- assert(symbol_id > 0, "a symbol handler must mark the symbol for writing");
- return symbol_id;
+ return 1;
+}
+
+traceid JfrSymbolId::mark(const Symbol* symbol, bool leakp) {
+ assert(symbol != NULL, "invariant");
+ return mark((uintptr_t)symbol->identity_hash(), symbol, leakp);
}
-traceid JfrSymbolId::mark(const Symbol* symbol) {
- assert(symbol != NULL, "invariant");
- return mark(symbol, (uintptr_t)const_cast<Symbol*>(symbol)->identity_hash());
-}
+static unsigned int last_symbol_hash = 0;
+static traceid last_symbol_id = 0;
-traceid JfrSymbolId::mark(const Symbol* data, uintptr_t hash) {
+traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
assert(data != NULL, "invariant");
assert(_sym_table != NULL, "invariant");
- return _sym_table->id(data, hash);
+ if (hash == last_symbol_hash) {
+ assert(last_symbol_id != 0, "invariant");
+ return last_symbol_id;
+ }
+ const SymbolEntry& entry = _sym_table->lookup_put(hash, data);
+ if (_class_unload) {
+ entry.set_unloading();
+ }
+ if (leakp) {
+ entry.set_leakp();
+ }
+ last_symbol_hash = hash;
+ last_symbol_id = entry.id();
+ return last_symbol_id;
}
-traceid JfrSymbolId::mark(const char* str, uintptr_t hash) {
+static unsigned int last_cstring_hash = 0;
+static traceid last_cstring_id = 0;
+
+traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
assert(str != NULL, "invariant");
- return _cstring_table->id(str, hash);
-}
-
-bool JfrSymbolId::is_unsafe_anonymous_klass(const Klass* k) {
- assert(k != NULL, "invariant");
- return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
+ assert(_cstring_table != NULL, "invariant");
+ if (hash == last_cstring_hash) {
+ assert(last_cstring_id != 0, "invariant");
+ return last_cstring_id;
+ }
+ const CStringEntry& entry = _cstring_table->lookup_put(hash, str);
+ if (_class_unload) {
+ entry.set_unloading();
+ }
+ if (leakp) {
+ entry.set_leakp();
+ }
+ last_cstring_hash = hash;
+ last_cstring_id = entry.id();
+ return last_cstring_id;
}
/*
@@ -161,7 +183,7 @@
* caller needs ResourceMark
*/
-uintptr_t JfrSymbolId::unsafe_anonymous_klass_name_hash_code(const InstanceKlass* ik) {
+uintptr_t JfrSymbolId::unsafe_anonymous_klass_name_hash(const InstanceKlass* ik) {
assert(ik != NULL, "invariant");
assert(ik->is_unsafe_anonymous(), "invariant");
const oop mirror = ik->java_mirror_no_keepalive();
@@ -169,19 +191,18 @@
return (uintptr_t)mirror->identity_hash();
}
-const char* JfrSymbolId::create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik, uintptr_t& hashcode) {
+static const char* create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
assert(ik != NULL, "invariant");
assert(ik->is_unsafe_anonymous(), "invariant");
- assert(0 == hashcode, "invariant");
+ assert(hash != 0, "invariant");
char* anonymous_symbol = NULL;
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
char hash_buf[40];
- hashcode = unsafe_anonymous_klass_name_hash_code(ik);
- sprintf(hash_buf, "/" UINTX_FORMAT, hashcode);
+ sprintf(hash_buf, "/" UINTX_FORMAT, hash);
const size_t hash_len = strlen(hash_buf);
const size_t result_len = ik->name()->utf8_length();
- anonymous_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
+ anonymous_symbol = JfrCHeapObj::new_array<char>(result_len + hash_len + 1);
ik->name()->as_klass_external_name(anonymous_symbol, (int)result_len + 1);
assert(strlen(anonymous_symbol) == result_len, "invariant");
strcpy(anonymous_symbol + result_len, hash_buf);
@@ -189,70 +210,102 @@
return anonymous_symbol;
}
-uintptr_t JfrSymbolId::regular_klass_name_hash_code(const Klass* k) {
+bool JfrSymbolId::is_unsafe_anonymous_klass(const Klass* k) {
assert(k != NULL, "invariant");
- const Symbol* const sym = k->name();
- assert(sym != NULL, "invariant");
- return (uintptr_t)const_cast<Symbol*>(sym)->identity_hash();
+ return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
+}
+
+static unsigned int last_anonymous_hash = 0;
+static traceid last_anonymous_id = 0;
+
+traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const InstanceKlass* ik, bool leakp) {
+ assert(ik != NULL, "invariant");
+ assert(ik->is_unsafe_anonymous(), "invariant");
+ const uintptr_t hash = unsafe_anonymous_klass_name_hash(ik);
+ if (hash == last_anonymous_hash) {
+ assert(last_anonymous_id != 0, "invariant");
+ return last_anonymous_id;
+ }
+ last_anonymous_hash = hash;
+ const CStringEntry* const entry = _cstring_table->lookup_only(hash);
+ last_anonymous_id = entry != NULL ? entry->id() : mark(hash, create_unsafe_anonymous_klass_symbol(ik, hash), leakp);
+ return last_anonymous_id;
+}
+
+traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
+ assert(k != NULL, "invariant");
+ traceid symbol_id = 0;
+ if (is_unsafe_anonymous_klass(k)) {
+ assert(k->is_instance_klass(), "invariant");
+ symbol_id = mark_unsafe_anonymous_klass_name((const InstanceKlass*)k, leakp);
+ }
+ if (0 == symbol_id) {
+ Symbol* const sym = k->name();
+ if (sym != NULL) {
+ symbol_id = mark(sym, leakp);
+ }
+ }
+ assert(symbol_id > 0, "a symbol handler must mark the symbol for writing");
+ return symbol_id;
+}
+
+static void reset_symbol_caches() {
+ last_anonymous_hash = 0;
+ last_symbol_hash = 0;
+ last_cstring_hash = 0;
}
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
- _klass_list(NULL),
- _class_unload(class_unload) {
+ _klass_list(NULL),
+ _total_count(0) {
initialize(class_unload);
assert(_klass_list != NULL, "invariant");
}
static const size_t initial_class_list_size = 200;
+
void JfrArtifactSet::initialize(bool class_unload) {
assert(_symbol_id != NULL, "invariant");
- _symbol_id->initialize();
- assert(!_symbol_id->has_entries(), "invariant");
- _symbol_id->mark(BOOTSTRAP_LOADER_NAME, 0); // pre-load "bootstrap"
- _class_unload = class_unload;
+ _symbol_id->set_class_unload(class_unload);
+ _total_count = 0;
// resource allocation
_klass_list = new GrowableArray<const Klass*>(initial_class_list_size, false, mtTracing);
}
JfrArtifactSet::~JfrArtifactSet() {
clear();
+ delete _symbol_id;
}
void JfrArtifactSet::clear() {
+ reset_symbol_caches();
_symbol_id->clear();
// _klass_list will be cleared by a ResourceMark
}
-traceid JfrArtifactSet::mark_unsafe_anonymous_klass_name(const Klass* klass) {
- return _symbol_id->mark_unsafe_anonymous_klass_name(klass);
+traceid JfrArtifactSet::bootstrap_name(bool leakp) {
+ return _symbol_id->bootstrap_name(leakp);
}
-traceid JfrArtifactSet::mark(const Symbol* sym, uintptr_t hash) {
- return _symbol_id->mark(sym, hash);
-}
-
-traceid JfrArtifactSet::mark(const Klass* klass) {
- return _symbol_id->mark(klass);
+traceid JfrArtifactSet::mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp) {
+ assert(klass->is_instance_klass(), "invariant");
+ return _symbol_id->mark_unsafe_anonymous_klass_name((const InstanceKlass*)klass, leakp);
}
-traceid JfrArtifactSet::mark(const Symbol* symbol) {
- return _symbol_id->mark(symbol);
-}
-
-traceid JfrArtifactSet::mark(const char* const str, uintptr_t hash) {
- return _symbol_id->mark(str, hash);
+traceid JfrArtifactSet::mark(uintptr_t hash, const Symbol* sym, bool leakp) {
+ return _symbol_id->mark(hash, sym, leakp);
}
-const JfrSymbolId::SymbolEntry* JfrArtifactSet::map_symbol(const Symbol* symbol) const {
- return _symbol_id->map_symbol(symbol);
+traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) {
+ return _symbol_id->mark(klass, leakp);
}
-const JfrSymbolId::SymbolEntry* JfrArtifactSet::map_symbol(uintptr_t hash) const {
- return _symbol_id->map_symbol(hash);
+traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) {
+ return _symbol_id->mark(symbol, leakp);
}
-const JfrSymbolId::CStringEntry* JfrArtifactSet::map_cstring(uintptr_t hash) const {
- return _symbol_id->map_cstring(hash);
+traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) {
+ return _symbol_id->mark(hash, str, leakp);
}
bool JfrArtifactSet::has_klass_entries() const {
@@ -269,3 +322,7 @@
assert(_klass_list->find(k) == -1, "invariant");
_klass_list->append(k);
}
+
+size_t JfrArtifactSet::total_count() const {
+ return _total_count;
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -76,223 +76,203 @@
};
template <typename T>
-void tag_leakp_artifact(T const& value, bool class_unload) {
- assert(value != NULL, "invariant");
- if (class_unload) {
- SET_LEAKP_USED_THIS_EPOCH(value);
- assert(LEAKP_USED_THIS_EPOCH(value), "invariant");
- } else {
- SET_LEAKP_USED_PREV_EPOCH(value);
- assert(LEAKP_USED_PREV_EPOCH(value), "invariant");
- }
-}
-
-template <typename T>
-class LeakpClearArtifact {
- bool _class_unload;
+class ClearArtifact {
public:
- LeakpClearArtifact(bool class_unload) : _class_unload(class_unload) {}
bool operator()(T const& value) {
- if (_class_unload) {
- if (LEAKP_USED_THIS_EPOCH(value)) {
- LEAKP_UNUSE_THIS_EPOCH(value);
- }
- } else {
- if (LEAKP_USED_PREV_EPOCH(value)) {
- LEAKP_UNUSE_PREV_EPOCH(value);
- }
- }
- return true;
- }
-};
-
-template <typename T>
-class ClearArtifact {
- bool _class_unload;
- public:
- ClearArtifact(bool class_unload) : _class_unload(class_unload) {}
- bool operator()(T const& value) {
- if (_class_unload) {
- if (LEAKP_USED_THIS_EPOCH(value)) {
- LEAKP_UNUSE_THIS_EPOCH(value);
- }
- if (USED_THIS_EPOCH(value)) {
- UNUSE_THIS_EPOCH(value);
- }
- if (METHOD_USED_THIS_EPOCH(value)) {
- UNUSE_METHOD_THIS_EPOCH(value);
- }
- } else {
- if (LEAKP_USED_PREV_EPOCH(value)) {
- LEAKP_UNUSE_PREV_EPOCH(value);
- }
- if (USED_PREV_EPOCH(value)) {
- UNUSE_PREV_EPOCH(value);
- }
- if (METHOD_USED_PREV_EPOCH(value)) {
- UNUSE_METHOD_PREV_EPOCH(value);
- }
- }
+ CLEAR_METHOD_AND_CLASS_PREV_EPOCH(value);
+ CLEAR_SERIALIZED(value);
+ assert(IS_NOT_SERIALIZED(value), "invariant");
return true;
}
};
template <>
class ClearArtifact<const Method*> {
+ public:
+ bool operator()(const Method* method) {
+ assert(METHOD_FLAG_USED_PREV_EPOCH(method), "invariant");
+ CLEAR_METHOD_FLAG_USED_PREV_EPOCH(method);
+ CLEAR_METHOD_SERIALIZED(method);
+ assert(METHOD_NOT_SERIALIZED(method), "invariant");
+ return true;
+ }
+};
+
+template <typename T>
+class Stub {
+ public:
+ bool operator()(T const& value) { return true; }
+};
+
+template <typename T>
+class SerializePredicate {
+ bool _class_unload;
+ public:
+ SerializePredicate(bool class_unload) : _class_unload(class_unload) {}
+ bool operator()(T const& value) {
+ assert(value != NULL, "invariant");
+ return _class_unload ? true : IS_NOT_SERIALIZED(value);
+ }
+};
+
+template <>
+class SerializePredicate<const Method*> {
bool _class_unload;
public:
- ClearArtifact(bool class_unload) : _class_unload(class_unload) {}
+ SerializePredicate(bool class_unload) : _class_unload(class_unload) {}
bool operator()(const Method* method) {
+ assert(method != NULL, "invariant");
+ return _class_unload ? true : METHOD_NOT_SERIALIZED(method);
+ }
+};
+
+template <typename T, bool leakp>
+class SymbolPredicate {
+ bool _class_unload;
+ public:
+ SymbolPredicate(bool class_unload) : _class_unload(class_unload) {}
+ bool operator()(T const& value) {
+ assert(value != NULL, "invariant");
if (_class_unload) {
- if (METHOD_FLAG_USED_THIS_EPOCH(method)) {
- CLEAR_METHOD_FLAG_USED_THIS_EPOCH(method);
- }
- } else {
- if (METHOD_FLAG_USED_PREV_EPOCH(method)) {
- CLEAR_METHOD_FLAG_USED_PREV_EPOCH(method);
- }
+ return leakp ? value->is_leakp() : value->is_unloading();
+ }
+ return leakp ? value->is_leakp() : !value->is_serialized();
+ }
+};
+
+template <bool leakp>
+class MethodUsedPredicate {
+ bool _current_epoch;
+public:
+ MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
+ bool operator()(const Klass* klass) {
+ if (_current_epoch) {
+ return leakp ? IS_LEAKP(klass) : METHOD_USED_THIS_EPOCH(klass);
}
- return true;
+ return leakp ? IS_LEAKP(klass) : METHOD_USED_PREV_EPOCH(klass);
+ }
+};
+
+template <bool leakp>
+class MethodFlagPredicate {
+ bool _current_epoch;
+ public:
+ MethodFlagPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
+ bool operator()(const Method* method) {
+ if (_current_epoch) {
+ return leakp ? IS_METHOD_LEAKP_USED(method) : METHOD_FLAG_USED_THIS_EPOCH(method);
+ }
+ return leakp ? IS_METHOD_LEAKP_USED(method) : METHOD_FLAG_USED_PREV_EPOCH(method);
}
};
template <typename T>
class LeakPredicate {
- bool _class_unload;
public:
- LeakPredicate(bool class_unload) : _class_unload(class_unload) {}
+ LeakPredicate(bool class_unload) {}
bool operator()(T const& value) {
- return _class_unload ? LEAKP_USED_THIS_EPOCH(value) : LEAKP_USED_PREV_EPOCH(value);
+ return IS_LEAKP(value);
}
};
-template <typename T>
-class UsedPredicate {
- bool _class_unload;
+template <>
+class LeakPredicate<const Method*> {
public:
- UsedPredicate(bool class_unload) : _class_unload(class_unload) {}
- bool operator()(T const& value) {
- return _class_unload ? USED_THIS_EPOCH(value) : USED_PREV_EPOCH(value);
+ LeakPredicate(bool class_unload) {}
+ bool operator()(const Method* method) {
+ assert(method != NULL, "invariant");
+ return IS_METHOD_LEAKP_USED(method);
}
};
-template <typename T, int compare(const T&, const T&)>
-class UniquePredicate {
- private:
- GrowableArray<T> _seen;
+template <typename T, typename IdType>
+class ListEntry : public JfrHashtableEntry<T, IdType> {
public:
- UniquePredicate(bool) : _seen() {}
- bool operator()(T const& value) {
- bool not_unique;
- _seen.template find_sorted<T, compare>(value, not_unique);
- if (not_unique) {
- return false;
- }
- _seen.template insert_sorted<compare>(value);
- return true;
+ ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry<T, IdType>(hash, data),
+ _list_next(NULL), _serialized(false), _unloading(false), _leakp(false) {}
+ const ListEntry<T, IdType>* list_next() const { return _list_next; }
+ void reset() const {
+ _list_next = NULL; _serialized = false; _unloading = false; _leakp = false;
}
-};
-
-class MethodFlagPredicate {
- bool _class_unload;
- public:
- MethodFlagPredicate(bool class_unload) : _class_unload(class_unload) {}
- bool operator()(const Method* method) {
- return _class_unload ? METHOD_FLAG_USED_THIS_EPOCH(method) : METHOD_FLAG_USED_PREV_EPOCH(method);
- }
-};
-
-template <bool leakp>
-class MethodUsedPredicate {
- bool _class_unload;
- public:
- MethodUsedPredicate(bool class_unload) : _class_unload(class_unload) {}
- bool operator()(const Klass* klass) {
- assert(ANY_USED(klass), "invariant");
- if (_class_unload) {
- return leakp ? LEAKP_METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_THIS_EPOCH(klass);
- }
- return leakp ? LEAKP_METHOD_USED_PREV_EPOCH(klass) : METHOD_USED_PREV_EPOCH(klass);
- }
+ void set_list_next(const ListEntry<T, IdType>* next) const { _list_next = next; }
+ bool is_serialized() const { return _serialized; }
+ void set_serialized() const { _serialized = true; }
+ bool is_unloading() const { return _unloading; }
+ void set_unloading() const { _unloading = true; }
+ bool is_leakp() const { return _leakp; }
+ void set_leakp() const { _leakp = true; }
+ private:
+ mutable const ListEntry<T, IdType>* _list_next;
+ mutable bool _serialized;
+ mutable bool _unloading;
+ mutable bool _leakp;
};
class JfrSymbolId : public JfrCHeapObj {
template <typename, typename, template<typename, typename> class, typename, size_t>
friend class HashTableHost;
- typedef HashTableHost<const Symbol*, traceid, Entry, JfrSymbolId> SymbolTable;
- typedef HashTableHost<const char*, traceid, Entry, JfrSymbolId> CStringTable;
+ typedef HashTableHost<const Symbol*, traceid, ListEntry, JfrSymbolId> SymbolTable;
+ typedef HashTableHost<const char*, traceid, ListEntry, JfrSymbolId> CStringTable;
+ friend class JfrArtifactSet;
public:
typedef SymbolTable::HashEntry SymbolEntry;
typedef CStringTable::HashEntry CStringEntry;
private:
SymbolTable* _sym_table;
CStringTable* _cstring_table;
+ const SymbolEntry* _sym_list;
+ const CStringEntry* _cstring_list;
traceid _symbol_id_counter;
+ bool _class_unload;
// hashtable(s) callbacks
- void assign_id(SymbolEntry* entry);
- bool equals(const Symbol* query, uintptr_t hash, const SymbolEntry* entry);
- void assign_id(CStringEntry* entry);
- bool equals(const char* query, uintptr_t hash, const CStringEntry* entry);
+ void on_link(const SymbolEntry* entry);
+ bool on_equals(uintptr_t hash, const SymbolEntry* entry);
+ void on_unlink(const SymbolEntry* entry);
+ void on_link(const CStringEntry* entry);
+ bool on_equals(uintptr_t hash, const CStringEntry* entry);
+ void on_unlink(const CStringEntry* entry);
+
+ template <typename Functor, typename T>
+ void iterate(Functor& functor, const T* list) {
+ const T* symbol = list;
+ while (symbol != NULL) {
+ const T* next = symbol->list_next();
+ functor(symbol);
+ symbol = next;
+ }
+ }
+
+ traceid mark_unsafe_anonymous_klass_name(const InstanceKlass* k, bool leakp);
+ bool is_unsafe_anonymous_klass(const Klass* k);
+ uintptr_t unsafe_anonymous_klass_name_hash(const InstanceKlass* ik);
public:
- static bool is_unsafe_anonymous_klass(const Klass* k);
- static const char* create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik, uintptr_t& hashcode);
- static uintptr_t unsafe_anonymous_klass_name_hash_code(const InstanceKlass* ik);
- static uintptr_t regular_klass_name_hash_code(const Klass* k);
-
JfrSymbolId();
~JfrSymbolId();
- void initialize();
void clear();
-
- traceid mark_unsafe_anonymous_klass_name(const Klass* k);
- traceid mark(const Symbol* sym, uintptr_t hash);
- traceid mark(const Klass* k);
- traceid mark(const Symbol* symbol);
- traceid mark(const char* str, uintptr_t hash);
+ void set_class_unload(bool class_unload);
- const SymbolEntry* map_symbol(const Symbol* symbol) const;
- const SymbolEntry* map_symbol(uintptr_t hash) const;
- const CStringEntry* map_cstring(uintptr_t hash) const;
+ traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
+ traceid mark(const Klass* k, bool leakp);
+ traceid mark(const Symbol* symbol, bool leakp);
+ traceid mark(uintptr_t hash, const char* str, bool leakp);
+ traceid bootstrap_name(bool leakp);
- template <typename T>
- void symbol(T& functor, const Klass* k) {
- if (is_unsafe_anonymous_klass(k)) {
- return;
- }
- functor(map_symbol(regular_klass_name_hash_code(k)));
+ template <typename Functor>
+ void iterate_symbols(Functor& functor) {
+ iterate(functor, _sym_list);
}
- template <typename T>
- void symbol(T& functor, const Method* method) {
- assert(method != NULL, "invariant");
- functor(map_symbol((uintptr_t)method->name()->identity_hash()));
- functor(map_symbol((uintptr_t)method->signature()->identity_hash()));
- }
-
- template <typename T>
- void cstring(T& functor, const Klass* k) {
- if (!is_unsafe_anonymous_klass(k)) {
- return;
- }
- functor(map_cstring(unsafe_anonymous_klass_name_hash_code((const InstanceKlass*)k)));
- }
-
- template <typename T>
- void iterate_symbols(T& functor) {
- _sym_table->iterate_entry(functor);
- }
-
- template <typename T>
- void iterate_cstrings(T& functor) {
- _cstring_table->iterate_entry(functor);
+ template <typename Functor>
+ void iterate_cstrings(Functor& functor) {
+ iterate(functor, _cstring_list);
}
bool has_entries() const { return has_symbol_entries() || has_cstring_entries(); }
- bool has_symbol_entries() const { return _sym_table->has_entries(); }
- bool has_cstring_entries() const { return _cstring_table->has_entries(); }
+ bool has_symbol_entries() const { return _sym_list != NULL; }
+ bool has_cstring_entries() const { return _cstring_list != NULL; }
};
/**
@@ -313,7 +293,7 @@
private:
JfrSymbolId* _symbol_id;
GrowableArray<const Klass*>* _klass_list;
- bool _class_unload;
+ size_t _total_count;
public:
JfrArtifactSet(bool class_unload);
@@ -323,11 +303,13 @@
void initialize(bool class_unload);
void clear();
- traceid mark(const Symbol* sym, uintptr_t hash);
- traceid mark(const Klass* klass);
- traceid mark(const Symbol* symbol);
- traceid mark(const char* const str, uintptr_t hash);
- traceid mark_unsafe_anonymous_klass_name(const Klass* klass);
+
+ traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
+ traceid mark(const Klass* klass, bool leakp);
+ traceid mark(const Symbol* symbol, bool leakp);
+ traceid mark(uintptr_t hash, const char* const str, bool leakp);
+ traceid mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp);
+ traceid bootstrap_name(bool leakp);
const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const;
const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const;
@@ -335,6 +317,7 @@
bool has_klass_entries() const;
int entries() const;
+ size_t total_count() const;
void register_klass(const Klass* k);
template <typename Functor>
@@ -355,6 +338,12 @@
void iterate_cstrings(T& functor) {
_symbol_id->iterate_cstrings(functor);
}
+
+ template <typename Writer>
+ void tally(Writer& writer) {
+ _total_count += writer.count();
+ }
+
};
class KlassArtifactRegistrator {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp Thu Sep 12 15:04:00 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2017, 2019, 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.
- *
- * 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.
- *
- */
-
-#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP
-#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP
-
-#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
-#include "memory/allocation.hpp"
-
-template <typename WriterImpl, u4 ID>
-class JfrArtifactWriterHost : public StackObj {
- private:
- WriterImpl _impl;
- JfrCheckpointWriter* _writer;
- JfrCheckpointContext _ctx;
- int64_t _count_offset;
- int _count;
- bool _skip_header;
- public:
- JfrArtifactWriterHost(JfrCheckpointWriter* writer,
- JfrArtifactSet* artifacts,
- bool class_unload,
- bool skip_header = false) : _impl(writer, artifacts, class_unload),
- _writer(writer),
- _ctx(writer->context()),
- _count(0),
- _skip_header(skip_header) {
- assert(_writer != NULL, "invariant");
- if (!_skip_header) {
- _writer->write_type((JfrTypeId)ID);
- _count_offset = _writer->reserve(sizeof(u4)); // Don't know how many yet
- }
- }
-
- ~JfrArtifactWriterHost() {
- if (_count == 0) {
- // nothing written, restore context for rewind
- _writer->set_context(_ctx);
- return;
- }
- assert(_count > 0, "invariant");
- if (!_skip_header) {
- _writer->write_count(_count, _count_offset);
- }
- }
-
- bool operator()(typename WriterImpl::Type const & value) {
- this->_count += _impl(value);
- return true;
- }
-
- int count() const { return _count; }
- void add(int count) { _count += count; }
-};
-
-typedef int(*artifact_write_operation)(JfrCheckpointWriter*, JfrArtifactSet*, const void*);
-
-template <typename T, artifact_write_operation op>
-class JfrArtifactWriterImplHost {
- private:
- JfrCheckpointWriter* _writer;
- JfrArtifactSet* _artifacts;
- bool _class_unload;
- public:
- typedef T Type;
- JfrArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) :
- _writer(writer), _artifacts(artifacts), _class_unload(class_unload) {}
- int operator()(T const& value) {
- return op(this->_writer, this->_artifacts, value);
- }
-};
-
-template <typename T, typename Predicate, artifact_write_operation op>
-class JfrPredicatedArtifactWriterImplHost : public JfrArtifactWriterImplHost<T, op> {
- private:
- Predicate _predicate;
- typedef JfrArtifactWriterImplHost<T, op> Parent;
- public:
- JfrPredicatedArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) :
- Parent(writer, artifacts, class_unload), _predicate(class_unload) {}
- int operator()(T const& value) {
- return _predicate(value) ? Parent::operator()(value) : 0;
- }
-};
-
-#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -159,7 +159,7 @@
// This mechanism will retain the event specific flags
// in the archive, allowing for event flag restoration
// when renewing the traceid on klass revival.
- k->set_trace_id(EVENT_FLAGS_MASK(k));
+ k->set_trace_id(EVENT_KLASS_MASK(k));
}
// used by CDS / APPCDS as part of "restore_unshareable_info"
@@ -181,12 +181,12 @@
return get(java_lang_Class::as_Klass(my_oop));
}
-traceid JfrTraceId::use(jclass jc, bool leakp /* false */) {
+traceid JfrTraceId::use(jclass jc) {
assert(jc != NULL, "invariant");
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_vm, "invariant");
const oop my_oop = JNIHandles::resolve(jc);
assert(my_oop != NULL, "invariant");
- return use(java_lang_Class::as_Klass(my_oop), leakp);
+ return use(java_lang_Class::as_Klass(my_oop));
}
bool JfrTraceId::in_visible_set(const jclass jc) {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -90,12 +90,16 @@
static traceid get(const Thread* thread);
// tag construct as used, returns pre-tagged traceid
- static traceid use(const Klass* klass, bool leakp = false);
- static traceid use(jclass jc, bool leakp = false);
- static traceid use(const Method* method, bool leakp = false);
- static traceid use(const ModuleEntry* module, bool leakp = false);
- static traceid use(const PackageEntry* package, bool leakp = false);
- static traceid use(const ClassLoaderData* cld, bool leakp = false);
+ static traceid use(const Klass* klass);
+ static traceid use(jclass jc);
+ static traceid use(const Method* method);
+ static traceid use(const Klass* klass, const Method* method);
+ static traceid use(const ModuleEntry* module);
+ static traceid use(const PackageEntry* package);
+ static traceid use(const ClassLoaderData* cld);
+
+ // leak profiler
+ static void set_leakp(const Method* method);
static void remove(const Klass* klass);
static void restore(const Klass* klass);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -28,8 +28,11 @@
#include "classfile/classLoaderData.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/packageEntry.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
+#include "jfr/support/jfrKlassExtension.hpp"
#include "oops/arrayKlass.hpp"
#include "oops/klass.hpp"
#include "oops/instanceKlass.hpp"
@@ -38,21 +41,11 @@
#include "utilities/debug.hpp"
template <typename T>
-inline traceid set_used_and_get(const T* type, bool leakp) {
+inline traceid set_used_and_get(const T* type) {
assert(type != NULL, "invariant");
- if (leakp) {
- SET_LEAKP_USED_THIS_EPOCH(type);
- assert(LEAKP_USED_THIS_EPOCH(type), "invariant");
- }
SET_USED_THIS_EPOCH(type);
assert(USED_THIS_EPOCH(type), "invariant");
- return TRACE_ID_MASKED_PTR(type);
-}
-
-template <typename T>
-inline traceid set_used_and_get_shifted(const T* type, bool leakp) {
- assert(type != NULL, "invariant");
- return set_used_and_get(type, leakp) >> TRACE_ID_SHIFT;
+ return TRACE_ID(type);
}
inline traceid JfrTraceId::get(const Klass* klass) {
@@ -65,38 +58,49 @@
return TRACE_ID_RAW(t->jfr_thread_local());
}
-inline traceid JfrTraceId::use(const Klass* klass, bool leakp /* false */) {
+inline traceid JfrTraceId::use(const Klass* klass) {
assert(klass != NULL, "invariant");
- return set_used_and_get_shifted(klass, leakp);
+ return set_used_and_get(klass);
}
-inline traceid JfrTraceId::use(const Method* method, bool leakp /* false */) {
+inline traceid JfrTraceId::use(const Method* method) {
+ assert(method != NULL, "invariant");
+ return use(method->method_holder(), method);
+}
+
+inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
+ assert(klass != NULL, "invariant");
assert(method != NULL, "invariant");
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
- const Klass* const klass = method->method_holder();
- assert(klass != NULL, "invariant");
- if (leakp) {
- SET_LEAKP_USED_THIS_EPOCH(klass);
- assert(LEAKP_USED_THIS_EPOCH(klass), "invariant");
- }
+
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
return (METHOD_ID(klass, method));
}
-inline traceid JfrTraceId::use(const ModuleEntry* module, bool leakp /* false */) {
+inline traceid JfrTraceId::use(const ModuleEntry* module) {
assert(module != NULL, "invariant");
- return set_used_and_get_shifted(module, leakp);
+ return set_used_and_get(module);
+}
+
+inline traceid JfrTraceId::use(const PackageEntry* package) {
+ assert(package != NULL, "invariant");
+ return set_used_and_get(package);
}
-inline traceid JfrTraceId::use(const PackageEntry* package, bool leakp /* false */) {
- assert(package != NULL, "invariant");
- return set_used_and_get_shifted(package, leakp);
+inline traceid JfrTraceId::use(const ClassLoaderData* cld) {
+ assert(cld != NULL, "invariant");
+ return cld->is_unsafe_anonymous() ? 0 : set_used_and_get(cld);
}
-inline traceid JfrTraceId::use(const ClassLoaderData* cld, bool leakp /* false */) {
- assert(cld != NULL, "invariant");
- return cld->is_unsafe_anonymous() ? 0 : set_used_and_get_shifted(cld, leakp);
+inline void JfrTraceId::set_leakp(const Method* method) {
+ assert(method != NULL, "invariant");
+ const Klass* const klass = method->method_holder();
+ assert(klass != NULL, "invariant");
+ assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
+ assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
+ SET_LEAKP(klass);
+ SET_METHOD_LEAKP(method);
}
inline bool JfrTraceId::in_visible_set(const Klass* klass) {
@@ -112,7 +116,7 @@
inline void JfrTraceId::tag_as_jdk_jfr_event(const Klass* klass) {
assert(klass != NULL, "invariant");
- SET_TAG(klass, JDK_JFR_EVENT_KLASS);
+ SET_JDK_JFR_EVENT_KLASS(klass);
assert(IS_JDK_JFR_EVENT_KLASS(klass), "invariant");
}
@@ -124,7 +128,7 @@
inline void JfrTraceId::tag_as_jdk_jfr_event_sub(const Klass* k) {
assert(k != NULL, "invariant");
if (IS_NOT_AN_EVENT_SUB_KLASS(k)) {
- SET_TAG(k, JDK_JFR_EVENT_SUBKLASS);
+ SET_JDK_JFR_EVENT_SUBKLASS(k);
}
assert(IS_JDK_JFR_EVENT_SUBKLASS(k), "invariant");
}
@@ -145,7 +149,7 @@
inline void JfrTraceId::tag_as_event_host(const Klass* k) {
assert(k != NULL, "invariant");
- SET_TAG(k, EVENT_HOST_KLASS);
+ SET_EVENT_HOST_KLASS(k);
assert(IS_EVENT_HOST_KLASS(k), "invariant");
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -27,20 +27,22 @@
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/atomic.hpp"
+#include "runtime/orderAccess.hpp"
#include "utilities/macros.hpp"
#ifdef VM_LITTLE_ENDIAN
static const int low_offset = 0;
-static const int leakp_offset = low_offset + 1;
+static const int meta_offset = low_offset + 1;
#else
static const int low_offset = 7;
-static const int leakp_offset = low_offset - 1;
+static const int meta_offset = low_offset - 1;
#endif
inline void set_bits(jbyte bits, jbyte* const dest) {
assert(dest != NULL, "invariant");
if (bits != (*dest & bits)) {
*dest |= bits;
+ OrderAccess::storestore();
}
}
@@ -92,16 +94,28 @@
set_mask(mask, ((jbyte*)dest) + low_offset);
}
-inline void set_leakp_traceid_bits(jbyte bits, traceid* dest) {
- set_bits(bits, ((jbyte*)dest) + leakp_offset);
+inline void set_meta_bits(jbyte bits, jbyte* const dest) {
+ assert(dest != NULL, "invariant");
+ *dest |= bits;
+}
+
+inline void set_traceid_meta_bits(jbyte bits, traceid* dest) {
+ set_meta_bits(bits, ((jbyte*)dest) + meta_offset);
}
-inline void set_leakp_traceid_bits_cas(jbyte bits, traceid* dest) {
- set_bits_cas(bits, ((jbyte*)dest) + leakp_offset);
+inline void set_meta_mask(jbyte mask, jbyte* const dest) {
+ assert(dest != NULL, "invariant");
+ *dest &= mask;
}
-inline void set_leakp_traceid_mask(jbyte mask, traceid* dest) {
- set_mask(mask, ((jbyte*)dest) + leakp_offset);
+inline void set_traceid_meta_mask(jbyte mask, traceid* dest) {
+ set_meta_mask(mask, ((jbyte*)dest) + meta_offset);
+}
+
+// only used by a single thread with no visibility requirements
+inline void clear_meta_bits(jbyte bits, jbyte* const dest) {
+ assert(dest != NULL, "invariant");
+ *dest ^= bits;
}
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBITS_INLINE_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -32,12 +32,8 @@
#define METHOD_USED_BIT (USED_BIT << 2)
#define EPOCH_1_SHIFT 0
#define EPOCH_2_SHIFT 1
-#define LEAKP_SHIFT 8
-
#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT)
#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT)
-#define LEAKP_USED_EPOCH_1_BIT (USED_EPOCH_1_BIT << LEAKP_SHIFT)
-#define LEAKP_USED_EPOCH_2_BIT (USED_EPOCH_2_BIT << LEAKP_SHIFT)
#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT)
#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT)
#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT)
@@ -75,14 +71,6 @@
return _epoch_state ? USED_EPOCH_1_BIT : USED_EPOCH_2_BIT;
}
- static traceid leakp_in_use_this_epoch_bit() {
- return _epoch_state ? LEAKP_USED_EPOCH_2_BIT : LEAKP_USED_EPOCH_1_BIT;
- }
-
- static traceid leakp_in_use_prev_epoch_bit() {
- return _epoch_state ? LEAKP_USED_EPOCH_1_BIT : LEAKP_USED_EPOCH_2_BIT;
- }
-
static traceid method_in_use_this_epoch_bit() {
return _epoch_state ? METHOD_USED_EPOCH_2_BIT : METHOD_USED_EPOCH_1_BIT;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,163 +25,117 @@
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDMACROS_HPP
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDMACROS_HPP
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp"
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
-#include "jfr/support/jfrKlassExtension.hpp"
-#include "utilities/globalDefinitions.hpp"
-
/**
*
* If a traceid is used, depending on epoch, either the first or the second bit is tagged.
* If a class member (method) is used, either the third or fourth bit is tagged.
* Which bit to set is a function of the epoch. This allows for concurrent tagging.
*
- * LeakProfiler subsystem gets its own byte and uses the same tagging scheme but is shifted up 8.
- *
- * We also tag the individual method by using the TraceFlag field,
+ * We also tag individual methods by using the _trace_flags field,
* (see jfr/support/jfrTraceIdExtension.hpp for details)
*
*/
-// these are defined in jfr/support/jfrKlassExtension.hpp
+// the following are defined in jfr/support/jfrKlassExtension.hpp
//
-// #define JDK_JFR_EVENT_SUBKLASS 16
-// #define JDK_JFR_EVENT_KLASS 32
-// #define EVENT_HOST_KLASS 64
-
-#define IS_JDK_JFR_EVENT_SUBKLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_SUBKLASS)) != 0)
+// #define JDK_JFR_EVENT_SUBKLASS 16
+// #define JDK_JFR_EVENT_KLASS 32
+// #define EVENT_HOST_KLASS 64
-#define ANY_USED_BITS (USED_EPOCH_2_BIT | \
- USED_EPOCH_1_BIT | \
- METHOD_USED_EPOCH_2_BIT | \
- METHOD_USED_EPOCH_1_BIT | \
- LEAKP_USED_EPOCH_2_BIT | \
- LEAKP_USED_EPOCH_1_BIT)
-
-#define TRACE_ID_META_BITS (EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS | ANY_USED_BITS)
-
-#define ANY_EVENT (EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)
-#define IS_JDK_JFR_EVENT_KLASS(ptr) (((ptr)->trace_id() & JDK_JFR_EVENT_KLASS) != 0)
-#define IS_EVENT_HOST_KLASS(ptr) (((ptr)->trace_id() & EVENT_HOST_KLASS) != 0)
-#define IS_NOT_AN_EVENT_KLASS(ptr) (!IS_EVENT_KLASS(ptr))
-#define IS_NOT_AN_EVENT_SUB_KLASS(ptr) (!IS_JDK_JFR_EVENT_SUBKLASS(ptr))
-#define IS_NOT_JDK_JFR_EVENT_KLASS(ptr) (!IS_JDK_JFR_EVENT_KLASS(ptr))
-#define EVENT_FLAGS_MASK(ptr) (((ptr)->trace_id() & ANY_EVENT) != 0)
-#define UNEVENT(ptr) ((ptr)->set_trace_id(((ptr)->trace_id()) & ~ANY_EVENT))
-
-#define TRACE_ID_SHIFT 16
+// static bits
+#define META_SHIFT 8
+#define LEAKP_META_BIT USED_BIT
+#define LEAKP_BIT (LEAKP_META_BIT << META_SHIFT)
+#define TRANSIENT_META_BIT (USED_BIT << 1)
+#define TRANSIENT_BIT (TRANSIENT_META_BIT << META_SHIFT)
+#define SERIALIZED_META_BIT (USED_BIT << 2)
+#define SERIALIZED_BIT (SERIALIZED_META_BIT << META_SHIFT)
+#define TRACE_ID_SHIFT 16
+#define METHOD_ID_NUM_MASK ((1 << TRACE_ID_SHIFT) - 1)
+#define META_BITS (SERIALIZED_BIT | TRANSIENT_BIT | LEAKP_BIT)
+#define EVENT_BITS (EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)
+#define USED_BITS (METHOD_USED_EPOCH_2_BIT | METHOD_USED_EPOCH_1_BIT | USED_EPOCH_2_BIT | USED_EPOCH_1_BIT)
+#define ALL_BITS (META_BITS | EVENT_BITS | USED_BITS)
+#define ALL_BITS_MASK (~(ALL_BITS))
-#define TRACE_ID_MASKED(id) (id & ~TRACE_ID_META_BITS)
-#define TRACE_ID_VALUE(id) (TRACE_ID_MASKED(id) >> TRACE_ID_SHIFT)
-#define TRACE_ID_MASKED_PTR(ptr) (TRACE_ID_MASKED((ptr)->trace_id()))
-#define TRACE_ID_RAW(ptr) ((ptr)->trace_id())
-#define TRACE_ID(ptr) (TRACE_ID_MASKED_PTR(ptr) >> TRACE_ID_SHIFT)
-#define METHOD_ID(kls, meth) (TRACE_ID_MASKED_PTR(kls) | (meth)->method_idnum())
-#define SET_TAG(ptr, tag) (set_traceid_bits(tag, (ptr)->trace_id_addr()))
-#define SET_LEAKP_TAG(ptr, tag) (set_leakp_traceid_bits(tag, (ptr)->trace_id_addr()))
-#define SET_TAG_CAS(ptr, tag) (set_traceid_bits_cas(tag, (ptr)->trace_id_addr()))
-#define SET_LEAKP_TAG_CAS(ptr, tag) (set_leakp_traceid_bits_cas(tag, (ptr)->trace_id_addr()))
-
-#define IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::in_use_this_epoch_bit())
-#define IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::in_use_prev_epoch_bit())
-#define LEAKP_IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::leakp_in_use_this_epoch_bit())
-#define LEAKP_IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::leakp_in_use_prev_epoch_bit())
+// epoch relative bits
+#define IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::in_use_this_epoch_bit())
+#define IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::in_use_prev_epoch_bit())
+#define METHOD_IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::method_in_use_this_epoch_bit())
+#define METHOD_IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::method_in_use_prev_epoch_bit())
+#define METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS (JfrTraceIdEpoch::method_and_class_in_use_this_epoch_bits())
+#define METHOD_AND_CLASS_IN_USE_PREV_EPOCH_BITS (JfrTraceIdEpoch::method_and_class_in_use_prev_epoch_bits())
+#define METHOD_FLAG_IN_USE_THIS_EPOCH_BIT ((jbyte)IN_USE_THIS_EPOCH_BIT)
+#define METHOD_FLAG_IN_USE_PREV_EPOCH_BIT ((jbyte)IN_USE_PREV_EPOCH_BIT)
-#define METHOD_IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::method_in_use_this_epoch_bit())
-#define METHOD_IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::method_in_use_prev_epoch_bit())
-#define METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS (JfrTraceIdEpoch::method_and_class_in_use_this_epoch_bits())
-#define METHOD_AND_CLASS_IN_USE_PREV_EPOCH_BITS (JfrTraceIdEpoch::method_and_class_in_use_prev_epoch_bits())
-
-#define UNUSE_THIS_EPOCH_MASK (~(IN_USE_THIS_EPOCH_BIT))
-#define UNUSE_PREV_EPOCH_MASK (~(IN_USE_PREV_EPOCH_BIT))
-#define LEAKP_UNUSE_THIS_EPOCH_MASK UNUSE_THIS_EPOCH_MASK
-#define LEAKP_UNUSE_PREV_EPOCH_MASK UNUSE_PREV_EPOCH_MASK
-
-#define UNUSE_METHOD_THIS_EPOCH_MASK (~(METHOD_IN_USE_THIS_EPOCH_BIT))
-#define UNUSE_METHOD_PREV_EPOCH_MASK (~(METHOD_IN_USE_PREV_EPOCH_BIT))
-#define LEAKP_UNUSE_METHOD_THIS_EPOCH_MASK (~(UNUSE_METHOD_THIS_EPOCH_MASK))
-#define LEAKP_UNUSE_METHOD_PREV_EPOCH_MASK (~UNUSE_METHOD_PREV_EPOCH_MASK))
-
-#define UNUSE_METHOD_AND_CLASS_THIS_EPOCH_MASK (~(METHOD_IN_USE_THIS_EPOCH_BIT | IN_USE_THIS_EPOCH_BIT))
-#define UNUSE_METHOD_AND_CLASS_PREV_EPOCH_MASK (~(METHOD_IN_USE_PREV_EPOCH_BIT | IN_USE_PREV_EPOCH_BIT))
-
-#define SET_USED_THIS_EPOCH(ptr) (SET_TAG(ptr, IN_USE_THIS_EPOCH_BIT))
-#define SET_USED_PREV_EPOCH(ptr) (SET_TAG_CAS(ptr, IN_USE_PREV_EPOCH_BIT))
-#define SET_LEAKP_USED_THIS_EPOCH(ptr) (SET_LEAKP_TAG(ptr, IN_USE_THIS_EPOCH_BIT))
-#define SET_LEAKP_USED_PREV_EPOCH(ptr) (SET_LEAKP_TAG_CAS(ptr, IN_USE_PREV_EPOCH_BIT))
-#define SET_METHOD_AND_CLASS_USED_THIS_EPOCH(kls) (SET_TAG(kls, METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS))
+// operators
+#define TRACE_ID_RAW(ptr) ((ptr)->trace_id())
+#define TRACE_ID(ptr) (TRACE_ID_RAW(ptr) >> TRACE_ID_SHIFT)
+#define TRACE_ID_MASKED(ptr) (TRACE_ID_RAW(ptr) & ALL_BITS_MASK)
+#define TRACE_ID_PREDICATE(ptr, bits) ((TRACE_ID_RAW(ptr) & bits) != 0)
+#define TRACE_ID_TAG(ptr, bits) (set_traceid_bits(bits, (ptr)->trace_id_addr()))
+#define TRACE_ID_TAG_CAS(ptr, bits) (set_traceid_bits_cas(bits, (ptr)->trace_id_addr()))
+#define TRACE_ID_CLEAR(ptr, bits) (set_traceid_mask(bits, (ptr)->trace_id_addr()))
+#define TRACE_ID_META_TAG(ptr, bits) (set_traceid_meta_bits(bits, (ptr)->trace_id_addr()))
+#define TRACE_ID_META_CLEAR(ptr, bits) (set_traceid_meta_mask(bits, (ptr)->trace_id_addr()))
+#define METHOD_ID(kls, method) (TRACE_ID_MASKED(kls) | (method)->orig_method_idnum())
+#define METHOD_FLAG_PREDICATE(method, bits) ((method)->is_trace_flag_set(bits))
+#define METHOD_FLAG_TAG(method, bits) (set_bits(bits, (method)->trace_flags_addr()))
+#define METHOD_META_TAG(method, bits) (set_meta_bits(bits, (method)->trace_meta_addr()))
+#define METHOD_FLAG_CLEAR(method, bits) (clear_bits_cas(bits, (method)->trace_flags_addr()))
+#define METHOD_META_CLEAR(method, bits) (set_meta_mask(bits, (method)->trace_meta_addr()))
-#define USED_THIS_EPOCH(ptr) (((ptr)->trace_id() & IN_USE_THIS_EPOCH_BIT) != 0)
-#define NOT_USED_THIS_EPOCH(ptr) (!USED_THIS_EPOCH(ptr))
-#define USED_PREV_EPOCH(ptr) (((ptr)->trace_id() & IN_USE_PREV_EPOCH_BIT) != 0)
-#define NOT_USED_PREV_EPOCH(ptr) (!USED_PREV_EPOCH(ptr))
-#define USED_ANY_EPOCH(ptr) (((ptr)->trace_id() & (USED_EPOCH_2_BIT | USED_EPOCH_1_BIT)) != 0)
-#define NOT_USED_ANY_EPOCH(ptr) (!USED_ANY_EPOCH(ptr))
-
-#define LEAKP_USED_THIS_EPOCH(ptr) (((ptr)->trace_id() & LEAKP_IN_USE_THIS_EPOCH_BIT) != 0)
-#define LEAKP_NOT_USED_THIS_EPOCH(ptr) (!LEAKP_USED_THIS_EPOCH(ptr))
-#define LEAKP_USED_PREV_EPOCH(ptr) (((ptr)->trace_id() & LEAKP_IN_USE_PREV_EPOCH_BIT) != 0)
-#define LEAKP_NOT_USED_PREV_EPOCH(ptr) (!LEAKP_USED_PREV_EPOCH(ptr))
-#define LEAKP_USED_ANY_EPOCH(ptr) (((ptr)->trace_id() & (LEAKP_USED_EPOCH_2_BIT | LEAKP_USED_EPOCH_1_BIT)) != 0)
-#define LEAKP_NOT_USED_ANY_EPOCH(ptr) (!LEAKP_USED_ANY_EPOCH(ptr))
+// predicates
+#define USED_THIS_EPOCH(ptr) (TRACE_ID_PREDICATE(ptr, (TRANSIENT_BIT | IN_USE_THIS_EPOCH_BIT)))
+#define NOT_USED_THIS_EPOCH(ptr) (!(USED_THIS_EPOCH(ptr)))
+#define USED_PREV_EPOCH(ptr) (TRACE_ID_PREDICATE(ptr, (TRANSIENT_BIT | IN_USE_PREV_EPOCH_BIT)))
+#define USED_ANY_EPOCH(ptr) (TRACE_ID_PREDICATE(ptr, (TRANSIENT_BIT | USED_EPOCH_2_BIT | USED_EPOCH_1_BIT)))
+#define METHOD_USED_THIS_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (METHOD_IN_USE_THIS_EPOCH_BIT)))
+#define METHOD_NOT_USED_THIS_EPOCH(kls) (!(METHOD_USED_THIS_EPOCH(kls)))
+#define METHOD_USED_PREV_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (METHOD_IN_USE_PREV_EPOCH_BIT)))
+#define METHOD_USED_ANY_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (METHOD_IN_USE_PREV_EPOCH_BIT | METHOD_IN_USE_THIS_EPOCH_BIT)))
+#define METHOD_AND_CLASS_USED_THIS_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS)))
+#define METHOD_AND_CLASS_USED_PREV_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (METHOD_AND_CLASS_IN_USE_PREV_EPOCH_BITS)))
+#define METHOD_AND_CLASS_USED_ANY_EPOCH(kls) (METHOD_USED_ANY_EPOCH(kls) && USED_ANY_EPOCH(kls))
+#define METHOD_FLAG_USED_THIS_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (METHOD_FLAG_IN_USE_THIS_EPOCH_BIT)))
+#define METHOD_FLAG_NOT_USED_THIS_EPOCH(method) (!(METHOD_FLAG_USED_THIS_EPOCH(method)))
+#define METHOD_FLAG_USED_PREV_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (METHOD_FLAG_IN_USE_PREV_EPOCH_BIT)))
-#define ANY_USED_THIS_EPOCH(ptr) (((ptr)->trace_id() & (LEAKP_IN_USE_THIS_EPOCH_BIT | IN_USE_THIS_EPOCH_BIT)) != 0)
-#define ANY_NOT_USED_THIS_EPOCH(ptr) (!ANY_USED_THIS_EPOCH(ptr))
-#define ANY_USED_PREV_EPOCH(ptr) (((ptr)->trace_id() & (LEAKP_IN_USE_PREV_EPOCH_BIT | IN_USE_PREV_EPOCH_BIT)) != 0)
-#define ANY_NOT_USED_PREV_EPOCH(ptr) (!ANY_USED_PREV_EPOCH(ptr))
-
-#define METHOD_USED_THIS_EPOCH(kls) (((kls)->trace_id() & METHOD_IN_USE_THIS_EPOCH_BIT) != 0)
-#define METHOD_NOT_USED_THIS_EPOCH(kls) (!METHOD_USED_THIS_EPOCH(kls))
-#define METHOD_USED_PREV_EPOCH(kls) (((kls)->trace_id() & METHOD_IN_USE_PREV_EPOCH_BIT) != 0)
-#define METHOD_NOT_USED_PREV_EPOCH(kls) (!METHOD_USED_PREV_EPOCH(kls))
-#define METHOD_USED_ANY_EPOCH(kls) (((kls)->trace_id() & (METHOD_IN_USE_PREV_EPOCH_BIT | METHOD_IN_USE_THIS_EPOCH_BIT)) != 0)
-
-#define METHOD_NOT_USED_ANY_EPOCH(kls) (!METHOD_USED_ANY_EPOCH(kls))
-
-#define METHOD_AND_CLASS_USED_THIS_EPOCH(kls) ((((kls)->trace_id() & METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS) == \
- METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS) != 0)
-
-#define METHOD_AND_CLASS_USED_PREV_EPOCH(kls) ((((kls)->trace_id() & METHOD_AND_CLASS_IN_USE_PREV_EPOCH_BITS) == \
- METHOD_AND_CLASS_IN_USE_PREV_EPOCH_BITS) != 0)
-
-#define METHOD_AND_CLASS_USED_ANY_EPOCH(kls) ((METHOD_USED_ANY_EPOCH(kls) && USED_ANY_EPOCH(kls)) != 0)
-#define METHOD_AND_CLASS_NOT_USED_ANY_EPOCH(kls) (!METHOD_AND_CLASS_USED_ANY_EPOCH(kls))
+// setters
+#define SET_USED_THIS_EPOCH(ptr) (TRACE_ID_TAG(ptr, IN_USE_THIS_EPOCH_BIT))
+#define SET_METHOD_AND_CLASS_USED_THIS_EPOCH(kls) (TRACE_ID_TAG(kls, METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS))
+#define SET_METHOD_FLAG_USED_THIS_EPOCH(method) (METHOD_FLAG_TAG(method, METHOD_FLAG_IN_USE_THIS_EPOCH_BIT))
+#define CLEAR_METHOD_AND_CLASS_PREV_EPOCH_MASK (~(METHOD_IN_USE_PREV_EPOCH_BIT | IN_USE_PREV_EPOCH_BIT))
+#define CLEAR_METHOD_AND_CLASS_PREV_EPOCH(kls) (TRACE_ID_CLEAR(kls, CLEAR_METHOD_AND_CLASS_PREV_EPOCH_MASK))
+#define CLEAR_METHOD_FLAG_USED_PREV_EPOCH(method) (METHOD_FLAG_CLEAR(method, METHOD_FLAG_IN_USE_PREV_EPOCH_BIT))
-#define LEAKP_METHOD_IN_USE_THIS_EPOCH (LEAKP_IN_USE_THIS_EPOCH_BIT | METHOD_IN_USE_THIS_EPOCH_BIT)
-#define LEAKP_METHOD_IN_USE_PREV_EPOCH (LEAKP_IN_USE_PREV_EPOCH_BIT | METHOD_IN_USE_PREV_EPOCH_BIT)
-#define LEAKP_METHOD_USED_THIS_EPOCH(ptr) ((((ptr)->trace_id() & LEAKP_METHOD_IN_USE_THIS_EPOCH) == \
- LEAKP_METHOD_IN_USE_THIS_EPOCH) != 0)
-#define LEAKP_METHOD_NOT_USED_THIS_EPOCH(kls) (!LEAKP_METHOD_USED_THIS_EPOCH(kls))
-#define LEAKP_METHOD_USED_PREV_EPOCH(ptr) ((((ptr)->trace_id() & LEAKP_METHOD_IN_USE_PREV_EPOCH) == \
- LEAKP_METHOD_IN_USE_PREV_EPOCH) != 0)
-#define LEAKP_METHOD_NOT_USED_PREV_EPOCH(kls) (!LEAKP_METHOD_USED_PREV_EPOCH(kls))
-
-#define UNUSE_THIS_EPOCH(ptr) (set_traceid_mask(UNUSE_THIS_EPOCH_MASK, (ptr)->trace_id_addr()))
-#define UNUSE_PREV_EPOCH(ptr) (set_traceid_mask(UNUSE_PREV_EPOCH_MASK, (ptr)->trace_id_addr()))
-#define UNUSE_METHOD_THIS_EPOCH(kls) (set_traceid_mask(UNUSE_METHOD_THIS_EPOCH_MASK, (kls)->trace_id_addr()))
-#define UNUSE_METHOD_PREV_EPOCH(kls) (set_traceid_mask(UNUSE_METHOD_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
+// types
+#define IS_JDK_JFR_EVENT_KLASS(kls) (TRACE_ID_PREDICATE(kls, JDK_JFR_EVENT_KLASS))
+#define IS_JDK_JFR_EVENT_SUBKLASS(kls) (TRACE_ID_PREDICATE(kls, JDK_JFR_EVENT_SUBKLASS))
+#define IS_NOT_AN_EVENT_SUB_KLASS(kls) (!(IS_JDK_JFR_EVENT_SUBKLASS(kls)))
+#define IS_EVENT_HOST_KLASS(kls) (TRACE_ID_PREDICATE(kls, EVENT_HOST_KLASS))
+#define SET_JDK_JFR_EVENT_KLASS(kls) (TRACE_ID_TAG(kls, JDK_JFR_EVENT_KLASS))
+#define SET_JDK_JFR_EVENT_SUBKLASS(kls) (TRACE_ID_TAG(kls, JDK_JFR_EVENT_SUBKLASS))
+#define SET_EVENT_HOST_KLASS(kls) (TRACE_ID_TAG(kls, EVENT_HOST_KLASS))
+#define EVENT_KLASS_MASK(kls) (TRACE_ID_RAW(kls) & EVENT_BITS)
-#define LEAKP_UNUSE_THIS_EPOCH(ptr) (set_leakp_traceid_mask(UNUSE_THIS_EPOCH_MASK, (ptr)->trace_id_addr()))
-#define LEAKP_UNUSE_PREV_EPOCH(ptr) (set_leakp_traceid_mask(UNUSE_PREV_EPOCH_MASK, (ptr)->trace_id_addr()))
-#define LEAKP_UNUSE_METHOD_THIS_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_THIS_EPOCH_MASK, (kls)->trace_id_addr()))
-#define LEAKP_UNUSE_METHOD_PREV_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
-
-#define ANY_USED(ptr) (((ptr)->trace_id() & ANY_USED_BITS) != 0)
-#define ANY_NOT_USED(ptr) (!ANY_USED(ptr))
-
-#define UNUSE_METHOD_AND_CLASS_THIS_EPOCH(kls) (set_traceid_mask(UNUSE_METHOD_AND_CLASS_THIS_EPOCH_MASK, (kls)->trace_id_addr()))
-#define LEAKP_UNUSE_METHOD_AND_CLASS_THIS_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_AND_CLASS_THIS_EPOCH_MASK, (kls)->trace_id_addr()))
-#define UNUSE_METHOD_AND_CLASS_PREV_EPOCH(kls) (set_traceid_mask(UNUSE_METHOD_AND_CLASS_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
-#define LEAKP_UNUSE_METHODS_AND_CLASS_PREV_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_AND_CLASS_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
-
-#define METHOD_FLAG_USED_THIS_EPOCH(m) ((m)->is_trace_flag_set((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit()))
-#define METHOD_FLAG_NOT_USED_THIS_EPOCH(m) (!METHOD_FLAG_USED_THIS_EPOCH(m))
-#define SET_METHOD_FLAG_USED_THIS_EPOCH(m) ((m)->set_trace_flag((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit()))
-#define METHOD_FLAG_USED_PREV_EPOCH(m) ((m)->is_trace_flag_set((jbyte)JfrTraceIdEpoch::in_use_prev_epoch_bit()))
-#define METHOD_FLAG_NOT_USED_PREV_EPOCH(m) (!METHOD_FLAG_USED_PREV_EPOCH(m))
-#define METHOD_FLAG_USED_ANY_EPOCH(m) ((METHOD_FLAG_USED_THIS_EPOCH(m) || METHOD_FLAG_USED_PREV_EPOCH(m)) != 0)
-#define METHOD_FLAG_NOT_USED_ANY_EPOCH(m) ((METHOD_FLAG_NOT_USED_THIS_EPOCH(m) && METHOD_FLAG_NOT_USED_PREV_EPOCH(m)) != 0)
-#define CLEAR_METHOD_FLAG_USED_THIS_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit(), (m)->trace_flags_addr()))
-#define CLEAR_METHOD_FLAG_USED_PREV_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_prev_epoch_bit(), (m)->trace_flags_addr()))
+// meta
+#define META_MASK (~(SERIALIZED_META_BIT | TRANSIENT_META_BIT | LEAKP_META_BIT))
+#define SET_LEAKP(ptr) (TRACE_ID_META_TAG(ptr, LEAKP_META_BIT))
+#define IS_LEAKP(ptr) (TRACE_ID_PREDICATE(ptr, LEAKP_BIT))
+#define SET_TRANSIENT(ptr) (TRACE_ID_META_TAG(ptr, TRANSIENT_META_BIT))
+#define IS_SERIALIZED(ptr) (TRACE_ID_PREDICATE(ptr, SERIALIZED_BIT))
+#define IS_NOT_SERIALIZED(ptr) (!(IS_SERIALIZED(ptr)))
+#define SHOULD_TAG(ptr) (NOT_USED_THIS_EPOCH(ptr))
+#define SHOULD_TAG_KLASS_METHOD(ptr) (METHOD_NOT_USED_THIS_EPOCH(ptr))
+#define SET_SERIALIZED(ptr) (TRACE_ID_META_TAG(ptr, SERIALIZED_META_BIT))
+#define CLEAR_SERIALIZED(ptr) (TRACE_ID_META_CLEAR(ptr, META_MASK))
+#define IS_METHOD_SERIALIZED(method) (METHOD_FLAG_PREDICATE(method, SERIALIZED_BIT))
+#define IS_METHOD_LEAKP_USED(method) (METHOD_FLAG_PREDICATE(method, LEAKP_BIT))
+#define METHOD_NOT_SERIALIZED(method) (!(IS_METHOD_SERIALIZED(method)))
+#define SET_METHOD_LEAKP(method) (METHOD_META_TAG(method, LEAKP_META_BIT))
+#define SET_METHOD_SERIALIZED(method) (METHOD_META_TAG(method, SERIALIZED_META_BIT))
+#define CLEAR_METHOD_SERIALIZED(method) (METHOD_META_CLEAR(method, META_MASK))
+#define CLEAR_LEAKP(ptr) (TRACE_ID_META_CLEAR(ptr, (~(LEAKP_META_BIT))))
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDMACROS_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkState.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -39,7 +39,7 @@
_start_nanos(0),
_previous_start_ticks(0),
_previous_start_nanos(0),
- _previous_checkpoint_offset(0) {}
+ _last_checkpoint_offset(0) {}
JfrChunkState::~JfrChunkState() {
reset();
@@ -50,15 +50,15 @@
JfrCHeapObj::free(_path, strlen(_path) + 1);
_path = NULL;
}
- set_previous_checkpoint_offset(0);
+ set_last_checkpoint_offset(0);
}
-void JfrChunkState::set_previous_checkpoint_offset(int64_t offset) {
- _previous_checkpoint_offset = offset;
+void JfrChunkState::set_last_checkpoint_offset(int64_t offset) {
+ _last_checkpoint_offset = offset;
}
-int64_t JfrChunkState::previous_checkpoint_offset() const {
- return _previous_checkpoint_offset;
+int64_t JfrChunkState::last_checkpoint_offset() const {
+ return _last_checkpoint_offset;
}
int64_t JfrChunkState::previous_start_ticks() const {
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkState.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -36,7 +36,7 @@
int64_t _start_nanos;
int64_t _previous_start_ticks;
int64_t _previous_start_nanos;
- int64_t _previous_checkpoint_offset;
+ int64_t _last_checkpoint_offset;
void update_start_ticks();
void update_start_nanos();
@@ -46,8 +46,8 @@
JfrChunkState();
~JfrChunkState();
void reset();
- int64_t previous_checkpoint_offset() const;
- void set_previous_checkpoint_offset(int64_t offset);
+ int64_t last_checkpoint_offset() const;
+ void set_last_checkpoint_offset(int64_t offset);
int64_t previous_start_ticks() const;
int64_t previous_start_nanos() const;
int64_t last_chunk_duration() const;
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -86,7 +86,7 @@
// Chunk size
this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET);
// initial checkpoint event offset
- this->write_be_at_offset(_chunkstate->previous_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
+ this->write_be_at_offset(_chunkstate->last_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
// metadata event offset
this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE));
// start of chunk in nanos since epoch
@@ -105,12 +105,12 @@
return this->is_valid() ? this->current_offset() : 0;
}
-int64_t JfrChunkWriter::previous_checkpoint_offset() const {
- return _chunkstate->previous_checkpoint_offset();
+int64_t JfrChunkWriter::last_checkpoint_offset() const {
+ return _chunkstate->last_checkpoint_offset();
}
-void JfrChunkWriter::set_previous_checkpoint_offset(int64_t offset) {
- _chunkstate->set_previous_checkpoint_offset(offset);
+void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) {
+ _chunkstate->set_last_checkpoint_offset(offset);
}
void JfrChunkWriter::time_stamp_chunk_now() {
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -49,8 +49,8 @@
JfrChunkWriter();
bool initialize();
int64_t size_written() const;
- int64_t previous_checkpoint_offset() const;
- void set_previous_checkpoint_offset(int64_t offset);
+ int64_t last_checkpoint_offset() const;
+ void set_last_checkpoint_offset(int64_t offset);
void time_stamp_chunk_now();
};
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -133,13 +133,13 @@
};
static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
- const int64_t prev_cp_offset = cw.previous_checkpoint_offset();
- const int64_t prev_cp_relative_offset = 0 == prev_cp_offset ? 0 : prev_cp_offset - cw.current_offset();
+ const int64_t last_cp_offset = cw.last_checkpoint_offset();
+ const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset();
cw.reserve(sizeof(u4));
cw.write<u8>(EVENT_CHECKPOINT);
cw.write(JfrTicks::now());
- cw.write((int64_t)0);
- cw.write(prev_cp_relative_offset); // write previous checkpoint offset delta
+ cw.write((int64_t)0); // duration
+ cw.write(delta_to_last_checkpoint);
cw.write<bool>(false); // flushpoint
cw.write((u4)1); // nof types in this checkpoint
cw.write(type_id);
@@ -178,7 +178,7 @@
_cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset);
_cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset);
// update writer with last checkpoint position
- _cw.set_previous_checkpoint_offset(current_cp_offset);
+ _cw.set_last_checkpoint_offset(current_cp_offset);
return true;
}
};
@@ -317,19 +317,16 @@
vm_error = true;
prepare_for_vm_error_rotation();
}
+ if (!_storage.control().to_disk()) {
+ in_memory_rotation();
+ } else if (vm_error) {
+ vm_error_rotation();
+ } else {
+ chunk_rotation();
+ }
if (msgs & (MSGBIT(MSG_STOP))) {
stop();
}
- // action determined by chunkwriter state
- if (!_chunkwriter.is_valid()) {
- in_memory_rotation();
- return;
- }
- if (vm_error) {
- vm_error_rotation();
- return;
- }
- chunk_rotation();
}
void JfrRecorderService::prepare_for_vm_error_rotation() {
@@ -400,12 +397,6 @@
WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo);
write_stack_trace_checkpoint.process();
}
-
-static void write_object_sample_stacktrace(ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repository) {
- WriteObjectSampleStacktrace object_sample_stacktrace(sampler, stack_trace_repository);
- object_sample_stacktrace.process();
-}
-
static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
WriteStringPool write_string_pool(string_pool);
WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
@@ -440,9 +431,7 @@
if (LeakProfiler::is_running()) {
// Exclusive access to the object sampler instance.
// The sampler is released (unlocked) later in post_safepoint_write.
- ObjectSampler* const sampler = ObjectSampler::acquire();
- assert(sampler != NULL, "invariant");
- write_object_sample_stacktrace(sampler, _stack_trace_repository);
+ ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository);
}
_storage.write();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2011, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
+#include "jfr/recorder/repository/jfrChunkWriter.hpp"
+#include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/vframe.inline.hpp"
+
+static void copy_frames(JfrStackFrame** lhs_frames, u4 length, const JfrStackFrame* rhs_frames) {
+ assert(lhs_frames != NULL, "invariant");
+ assert(rhs_frames != NULL, "invariant");
+ if (length > 0) {
+ *lhs_frames = NEW_C_HEAP_ARRAY(JfrStackFrame, length, mtTracing);
+ memcpy(*lhs_frames, rhs_frames, length * sizeof(JfrStackFrame));
+ }
+}
+
+JfrStackFrame::JfrStackFrame(const traceid& id, int bci, int type, const Method* method) :
+ _method(method), _methodid(id), _line(0), _bci(bci), _type(type) {}
+
+JfrStackFrame::JfrStackFrame(const traceid& id, int bci, int type, int lineno) :
+ _method(NULL), _methodid(id), _line(lineno), _bci(bci), _type(type) {}
+
+JfrStackTrace::JfrStackTrace(JfrStackFrame* frames, u4 max_frames) :
+ _next(NULL),
+ _frames(frames),
+ _id(0),
+ _hash(0),
+ _nr_of_frames(0),
+ _max_frames(max_frames),
+ _frames_ownership(false),
+ _reached_root(false),
+ _lineno(false),
+ _written(false) {}
+
+JfrStackTrace::JfrStackTrace(traceid id, const JfrStackTrace& trace, const JfrStackTrace* next) :
+ _next(next),
+ _frames(NULL),
+ _id(id),
+ _hash(trace._hash),
+ _nr_of_frames(trace._nr_of_frames),
+ _max_frames(trace._max_frames),
+ _frames_ownership(true),
+ _reached_root(trace._reached_root),
+ _lineno(trace._lineno),
+ _written(false) {
+ copy_frames(&_frames, trace._nr_of_frames, trace._frames);
+}
+
+JfrStackTrace::~JfrStackTrace() {
+ if (_frames_ownership) {
+ FREE_C_HEAP_ARRAY(JfrStackFrame, _frames);
+ }
+}
+
+template <typename Writer>
+static void write_stacktrace(Writer& w, traceid id, bool reached_root, u4 nr_of_frames, const JfrStackFrame* frames) {
+ w.write((u8)id);
+ w.write((u1)!reached_root);
+ w.write(nr_of_frames);
+ for (u4 i = 0; i < nr_of_frames; ++i) {
+ frames[i].write(w);
+ }
+}
+
+void JfrStackTrace::write(JfrChunkWriter& sw) const {
+ assert(!_written, "invariant");
+ write_stacktrace(sw, _id, _reached_root, _nr_of_frames, _frames);
+ _written = true;
+}
+
+void JfrStackTrace::write(JfrCheckpointWriter& cpw) const {
+ write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames);
+}
+
+bool JfrStackFrame::equals(const JfrStackFrame& rhs) const {
+ return _methodid == rhs._methodid && _bci == rhs._bci && _type == rhs._type;
+}
+
+bool JfrStackTrace::equals(const JfrStackTrace& rhs) const {
+ if (_reached_root != rhs._reached_root || _nr_of_frames != rhs._nr_of_frames || _hash != rhs._hash) {
+ return false;
+ }
+ for (u4 i = 0; i < _nr_of_frames; ++i) {
+ if (!_frames[i].equals(rhs._frames[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename Writer>
+static void write_frame(Writer& w, traceid methodid, int line, int bci, u1 type) {
+ w.write((u8)methodid);
+ w.write((u4)line);
+ w.write((u4)bci);
+ w.write((u8)type);
+}
+
+void JfrStackFrame::write(JfrChunkWriter& cw) const {
+ write_frame(cw, _methodid, _line, _bci, _type);
+}
+
+void JfrStackFrame::write(JfrCheckpointWriter& cpw) const {
+ write_frame(cpw, _methodid, _line, _bci, _type);
+}
+
+class vframeStreamSamples : public vframeStreamCommon {
+ public:
+ // constructor that starts with sender of frame fr (top_frame)
+ vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) {
+ _stop_at_java_call_stub = stop_at_java_call_stub;
+ _frame = fr;
+
+ // We must always have a valid frame to start filling
+ bool filled_in = fill_from_frame();
+ assert(filled_in, "invariant");
+ }
+ void samples_next();
+ void stop() {}
+};
+
+// Solaris SPARC Compiler1 needs an additional check on the grandparent
+// of the top_frame when the parent of the top_frame is interpreted and
+// the grandparent is compiled. However, in this method we do not know
+// the relationship of the current _frame relative to the top_frame so
+// we implement a more broad sanity check. When the previous callee is
+// interpreted and the current sender is compiled, we verify that the
+// current sender is also walkable. If it is not walkable, then we mark
+// the current vframeStream as at the end.
+void vframeStreamSamples::samples_next() {
+ // handle frames with inlining
+ if (_mode == compiled_mode &&
+ vframeStreamCommon::fill_in_compiled_inlined_sender()) {
+ return;
+ }
+
+ // handle general case
+ u4 loop_count = 0;
+ u4 loop_max = MAX_STACK_DEPTH * 2;
+ do {
+ loop_count++;
+ // By the time we get here we should never see unsafe but better safe then segv'd
+ if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) {
+ _mode = at_end_mode;
+ return;
+ }
+ _frame = _frame.sender(&_reg_map);
+ } while (!fill_from_frame());
+}
+
+bool JfrStackTrace::record_thread(JavaThread& thread, frame& frame) {
+ vframeStreamSamples st(&thread, frame, false);
+ u4 count = 0;
+ _reached_root = true;
+
+ while (!st.at_end()) {
+ if (count >= _max_frames) {
+ _reached_root = false;
+ break;
+ }
+ const Method* method = st.method();
+ if (!Method::is_valid_method(method)) {
+ // we throw away everything we've gathered in this sample since
+ // none of it is safe
+ return false;
+ }
+ const traceid mid = JfrTraceId::use(method);
+ int type = st.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
+ int bci = 0;
+ if (method->is_native()) {
+ type = JfrStackFrame::FRAME_NATIVE;
+ } else {
+ bci = st.bci();
+ }
+ const int lineno = method->line_number_from_bci(bci);
+ // Can we determine if it's inlined?
+ _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
+ _frames[count] = JfrStackFrame(mid, bci, type, method);
+ st.samples_next();
+ count++;
+ }
+
+ _lineno = true;
+ _nr_of_frames = count;
+ return true;
+}
+
+void JfrStackFrame::resolve_lineno() const {
+ assert(_method, "no method pointer");
+ assert(_line == 0, "already have linenumber");
+ _line = _method->line_number_from_bci(_bci);
+}
+
+void JfrStackTrace::resolve_linenos() const {
+ for (unsigned int i = 0; i < _nr_of_frames; i++) {
+ _frames[i].resolve_lineno();
+ }
+ _lineno = true;
+}
+
+bool JfrStackTrace::record_safe(JavaThread* thread, int skip) {
+ assert(thread == Thread::current(), "Thread stack needs to be walkable");
+ vframeStream vfs(thread);
+ u4 count = 0;
+ _reached_root = true;
+ for (int i = 0; i < skip; i++) {
+ if (vfs.at_end()) {
+ break;
+ }
+ vfs.next();
+ }
+
+ while (!vfs.at_end()) {
+ if (count >= _max_frames) {
+ _reached_root = false;
+ break;
+ }
+ const Method* method = vfs.method();
+ const traceid mid = JfrTraceId::use(method);
+ int type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
+ int bci = 0;
+ if (method->is_native()) {
+ type = JfrStackFrame::FRAME_NATIVE;
+ }
+ else {
+ bci = vfs.bci();
+ }
+ // Can we determine if it's inlined?
+ _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
+ _frames[count] = JfrStackFrame(mid, bci, type, method);
+ vfs.next();
+ count++;
+ }
+
+ _nr_of_frames = count;
+ return true;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACE_HPP
+#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACE_HPP
+
+#include "jfr/utilities/jfrAllocation.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+
+class frame;
+class JavaThread;
+class JfrCheckpointWriter;
+class JfrChunkWriter;
+class Method;
+
+class JfrStackFrame {
+ friend class ObjectSampleCheckpoint;
+ private:
+ const Method* _method;
+ traceid _methodid;
+ mutable int _line;
+ int _bci;
+ u1 _type;
+
+ public:
+ JfrStackFrame(const traceid& id, int bci, int type, const Method* method);
+ JfrStackFrame(const traceid& id, int bci, int type, int lineno);
+
+ bool equals(const JfrStackFrame& rhs) const;
+ void write(JfrChunkWriter& cw) const;
+ void write(JfrCheckpointWriter& cpw) const;
+ void resolve_lineno() const;
+
+ enum {
+ FRAME_INTERPRETER = 0,
+ FRAME_JIT,
+ FRAME_INLINE,
+ FRAME_NATIVE,
+ NUM_FRAME_TYPES
+ };
+};
+
+class JfrStackTrace : public JfrCHeapObj {
+ friend class JfrNativeSamplerCallback;
+ friend class JfrStackTraceRepository;
+ friend class ObjectSampleCheckpoint;
+ friend class ObjectSampler;
+ friend class OSThreadSampler;
+ friend class StackTraceResolver;
+ private:
+ const JfrStackTrace* _next;
+ JfrStackFrame* _frames;
+ traceid _id;
+ unsigned int _hash;
+ u4 _nr_of_frames;
+ u4 _max_frames;
+ bool _frames_ownership;
+ bool _reached_root;
+ mutable bool _lineno;
+ mutable bool _written;
+
+ const JfrStackTrace* next() const { return _next; }
+
+ bool should_write() const { return !_written; }
+ void write(JfrChunkWriter& cw) const;
+ void write(JfrCheckpointWriter& cpw) const;
+ bool equals(const JfrStackTrace& rhs) const;
+
+ void set_id(traceid id) { _id = id; }
+ void set_nr_of_frames(u4 nr_of_frames) { _nr_of_frames = nr_of_frames; }
+ void set_hash(unsigned int hash) { _hash = hash; }
+ void set_reached_root(bool reached_root) { _reached_root = reached_root; }
+ void resolve_linenos() const;
+
+ bool record_thread(JavaThread& thread, frame& frame);
+ bool record_safe(JavaThread* thread, int skip);
+
+ bool have_lineno() const { return _lineno; }
+ bool full_stacktrace() const { return _reached_root; }
+
+ JfrStackTrace(traceid id, const JfrStackTrace& trace, const JfrStackTrace* next);
+ JfrStackTrace(JfrStackFrame* frames, u4 max_frames);
+ ~JfrStackTrace();
+
+ public:
+ unsigned int hash() const { return _hash; }
+ traceid id() const { return _id; }
+};
+
+#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACE_HPP
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -24,66 +24,18 @@
#include "precompiled.hpp"
#include "jfr/metadata/jfrSerializer.hpp"
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
-#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
-#include "memory/allocation.inline.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
#include "runtime/mutexLocker.hpp"
-#include "runtime/os.inline.hpp"
-#include "runtime/safepoint.hpp"
-#include "runtime/task.hpp"
-#include "runtime/vframe.inline.hpp"
-
-class vframeStreamSamples : public vframeStreamCommon {
- public:
- // constructor that starts with sender of frame fr (top_frame)
- vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub);
- void samples_next();
- void stop() {}
-};
-
-vframeStreamSamples::vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) {
- _stop_at_java_call_stub = stop_at_java_call_stub;
- _frame = fr;
-
- // We must always have a valid frame to start filling
- bool filled_in = fill_from_frame();
- assert(filled_in, "invariant");
-}
-
-// Solaris SPARC Compiler1 needs an additional check on the grandparent
-// of the top_frame when the parent of the top_frame is interpreted and
-// the grandparent is compiled. However, in this method we do not know
-// the relationship of the current _frame relative to the top_frame so
-// we implement a more broad sanity check. When the previous callee is
-// interpreted and the current sender is compiled, we verify that the
-// current sender is also walkable. If it is not walkable, then we mark
-// the current vframeStream as at the end.
-void vframeStreamSamples::samples_next() {
- // handle frames with inlining
- if (_mode == compiled_mode &&
- vframeStreamCommon::fill_in_compiled_inlined_sender()) {
- return;
- }
-
- // handle general case
- u4 loop_count = 0;
- u4 loop_max = MAX_STACK_DEPTH * 2;
- do {
- loop_count++;
- // By the time we get here we should never see unsafe but better safe then segv'd
- if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) {
- _mode = at_end_mode;
- return;
- }
- _frame = _frame.sender(&_reg_map);
- } while (!fill_from_frame());
-}
static JfrStackTraceRepository* _instance = NULL;
+JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) {
+ memset(_table, 0, sizeof(_table));
+}
+
JfrStackTraceRepository& JfrStackTraceRepository::instance() {
return *_instance;
}
@@ -94,15 +46,6 @@
return _instance;
}
-void JfrStackTraceRepository::destroy() {
- assert(_instance != NULL, "invarinat");
- delete _instance;
- _instance = NULL;
-}
-
-JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) {
- memset(_table, 0, sizeof(_table));
-}
class JfrFrameType : public JfrSerializer {
public:
void serialize(JfrCheckpointWriter& writer) {
@@ -122,100 +65,10 @@
return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
}
-size_t JfrStackTraceRepository::clear() {
- MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
- if (_entries == 0) {
- return 0;
- }
- for (u4 i = 0; i < TABLE_SIZE; ++i) {
- JfrStackTraceRepository::StackTrace* stacktrace = _table[i];
- while (stacktrace != NULL) {
- JfrStackTraceRepository::StackTrace* next = stacktrace->next();
- delete stacktrace;
- stacktrace = next;
- }
- }
- memset(_table, 0, sizeof(_table));
- const size_t processed = _entries;
- _entries = 0;
- return processed;
-}
-
-traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
- MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
- const size_t index = stacktrace._hash % TABLE_SIZE;
- const StackTrace* table_entry = _table[index];
-
- while (table_entry != NULL) {
- if (table_entry->equals(stacktrace)) {
- return table_entry->id();
- }
- table_entry = table_entry->next();
- }
-
- if (!stacktrace.have_lineno()) {
- return 0;
- }
-
- traceid id = ++_next_id;
- _table[index] = new StackTrace(id, stacktrace, _table[index]);
- ++_entries;
- return id;
-}
-
-traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
- traceid tid = instance().add_trace(stacktrace);
- if (tid == 0) {
- stacktrace.resolve_linenos();
- tid = instance().add_trace(stacktrace);
- }
- assert(tid != 0, "invariant");
- return tid;
-}
-
-traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
- assert(thread == Thread::current(), "invariant");
- JfrThreadLocal* const tl = thread->jfr_thread_local();
- assert(tl != NULL, "invariant");
- if (tl->has_cached_stack_trace()) {
- return tl->cached_stack_trace_id();
- }
- if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
- return 0;
- }
- JfrStackFrame* frames = tl->stackframes();
- if (frames == NULL) {
- // pending oom
- return 0;
- }
- assert(frames != NULL, "invariant");
- assert(tl->stackframes() == frames, "invariant");
- return instance().record_for((JavaThread*)thread, skip,frames, tl->stackdepth());
-}
-
-traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) {
- JfrStackTrace stacktrace(frames, max_frames);
- return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0;
-}
-
-traceid JfrStackTraceRepository::add(const JfrStackTrace* stacktrace, JavaThread* thread) {
- assert(stacktrace != NULL, "invariant");
- assert(thread != NULL, "invariant");
- assert(stacktrace->hash() != 0, "invariant");
- return add(*stacktrace);
-}
-
-bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip) {
- assert(thread == Thread::current(), "invariant");
- assert(stacktrace != NULL, "invariant");
- JfrThreadLocal* const tl = thread->jfr_thread_local();
- assert(tl != NULL, "invariant");
- const unsigned int cached_stacktrace_hash = tl->cached_stack_trace_hash();
- if (cached_stacktrace_hash != 0) {
- stacktrace->set_hash(cached_stacktrace_hash);
- return true;
- }
- return stacktrace->record_safe(thread, skip, true);
+void JfrStackTraceRepository::destroy() {
+ assert(_instance != NULL, "invarinat");
+ delete _instance;
+ _instance = NULL;
}
size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
@@ -223,9 +76,9 @@
assert(_entries > 0, "invariant");
int count = 0;
for (u4 i = 0; i < TABLE_SIZE; ++i) {
- JfrStackTraceRepository::StackTrace* stacktrace = _table[i];
+ JfrStackTrace* stacktrace = _table[i];
while (stacktrace != NULL) {
- JfrStackTraceRepository::StackTrace* next = stacktrace->next();
+ JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
if (stacktrace->should_write()) {
stacktrace->write(sw);
++count;
@@ -249,7 +102,7 @@
traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) {
assert(JfrStacktrace_lock->owned_by_self(), "invariant");
- const StackTrace* const trace = resolve_entry(hash, id);
+ const JfrStackTrace* const trace = lookup(hash, id);
assert(trace != NULL, "invariant");
assert(trace->hash() == hash, "invariant");
assert(trace->id() == id, "invariant");
@@ -257,82 +110,105 @@
return id;
}
-JfrStackTraceRepository::StackTrace::StackTrace(traceid id, const JfrStackTrace& trace, JfrStackTraceRepository::StackTrace* next) :
- _next(next),
- _frames(NULL),
- _id(id),
- _nr_of_frames(trace._nr_of_frames),
- _hash(trace._hash),
- _reached_root(trace._reached_root),
- _written(false) {
- if (_nr_of_frames > 0) {
- _frames = NEW_C_HEAP_ARRAY(JfrStackFrame, _nr_of_frames, mtTracing);
- memcpy(_frames, trace._frames, _nr_of_frames * sizeof(JfrStackFrame));
+void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
+ JfrFrameType fct;
+ writer.write_type(TYPE_FRAMETYPE);
+ fct.serialize(writer);
+}
+
+size_t JfrStackTraceRepository::clear() {
+ MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
+ if (_entries == 0) {
+ return 0;
+ }
+ for (u4 i = 0; i < TABLE_SIZE; ++i) {
+ JfrStackTrace* stacktrace = _table[i];
+ while (stacktrace != NULL) {
+ JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
+ delete stacktrace;
+ stacktrace = next;
+ }
+ }
+ memset(_table, 0, sizeof(_table));
+ const size_t processed = _entries;
+ _entries = 0;
+ return processed;
+}
+
+traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
+ assert(thread == Thread::current(), "invariant");
+ JfrThreadLocal* const tl = thread->jfr_thread_local();
+ assert(tl != NULL, "invariant");
+ if (tl->has_cached_stack_trace()) {
+ return tl->cached_stack_trace_id();
+ }
+ if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
+ return 0;
+ }
+ JfrStackFrame* frames = tl->stackframes();
+ if (frames == NULL) {
+ // pending oom
+ return 0;
+ }
+ assert(frames != NULL, "invariant");
+ assert(tl->stackframes() == frames, "invariant");
+ return instance().record_for((JavaThread*)thread, skip, frames, tl->stackdepth());
+}
+
+traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) {
+ JfrStackTrace stacktrace(frames, max_frames);
+ return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0;
+}
+
+traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
+ traceid tid = instance().add_trace(stacktrace);
+ if (tid == 0) {
+ stacktrace.resolve_linenos();
+ tid = instance().add_trace(stacktrace);
+ }
+ assert(tid != 0, "invariant");
+ return tid;
+}
+
+void JfrStackTraceRepository::record_and_cache(JavaThread* thread, int skip /* 0 */) {
+ assert(thread != NULL, "invariant");
+ JfrThreadLocal* const tl = thread->jfr_thread_local();
+ assert(tl != NULL, "invariant");
+ assert(!tl->has_cached_stack_trace(), "invariant");
+ JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth());
+ stacktrace.record_safe(thread, skip);
+ const unsigned int hash = stacktrace.hash();
+ if (hash != 0) {
+ tl->set_cached_stack_trace_id(instance().add(stacktrace), hash);
}
}
-JfrStackTraceRepository::StackTrace::~StackTrace() {
- FREE_C_HEAP_ARRAY(JfrStackFrame, _frames);
-}
-
-bool JfrStackTraceRepository::StackTrace::equals(const JfrStackTrace& rhs) const {
- if (_reached_root != rhs._reached_root || _nr_of_frames != rhs._nr_of_frames || _hash != rhs._hash) {
- return false;
- }
- for (u4 i = 0; i < _nr_of_frames; ++i) {
- if (!_frames[i].equals(rhs._frames[i])) {
- return false;
- }
- }
- return true;
-}
-
-template <typename Writer>
-static void write_stacktrace(Writer& w, traceid id, bool reached_root, u4 nr_of_frames, const JfrStackFrame* frames) {
- w.write((u8)id);
- w.write((u1)!reached_root);
- w.write(nr_of_frames);
- for (u4 i = 0; i < nr_of_frames; ++i) {
- frames[i].write(w);
- }
-}
+traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
+ MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
+ const size_t index = stacktrace._hash % TABLE_SIZE;
+ const JfrStackTrace* table_entry = _table[index];
-void JfrStackTraceRepository::StackTrace::write(JfrChunkWriter& sw) const {
- assert(!_written, "invariant");
- write_stacktrace(sw, _id, _reached_root, _nr_of_frames, _frames);
- _written = true;
-}
-
-void JfrStackTraceRepository::StackTrace::write(JfrCheckpointWriter& cpw) const {
- write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames);
-}
-
-// JfrStackFrame
+ while (table_entry != NULL) {
+ if (table_entry->equals(stacktrace)) {
+ return table_entry->id();
+ }
+ table_entry = table_entry->next();
+ }
-bool JfrStackFrame::equals(const JfrStackFrame& rhs) const {
- return _methodid == rhs._methodid && _bci == rhs._bci && _type == rhs._type;
-}
+ if (!stacktrace.have_lineno()) {
+ return 0;
+ }
-template <typename Writer>
-static void write_frame(Writer& w, traceid methodid, int line, int bci, u1 type) {
- w.write((u8)methodid);
- w.write((u4)line);
- w.write((u4)bci);
- w.write((u8)type);
-}
-
-void JfrStackFrame::write(JfrChunkWriter& cw) const {
- write_frame(cw, _methodid, _line, _bci, _type);
-}
-
-void JfrStackFrame::write(JfrCheckpointWriter& cpw) const {
- write_frame(cpw, _methodid, _line, _bci, _type);
+ traceid id = ++_next_id;
+ _table[index] = new JfrStackTrace(id, stacktrace, _table[index]);
+ ++_entries;
+ return id;
}
// invariant is that the entry to be resolved actually exists in the table
-const JfrStackTraceRepository::StackTrace* JfrStackTraceRepository::resolve_entry(unsigned int hash, traceid id) const {
+const JfrStackTrace* JfrStackTraceRepository::lookup(unsigned int hash, traceid id) const {
const size_t index = (hash % TABLE_SIZE);
- const StackTrace* trace = _table[index];
+ const JfrStackTrace* trace = _table[index];
while (trace != NULL && trace->id() != id) {
trace = trace->next();
}
@@ -341,102 +217,3 @@
assert(trace->id() == id, "invariant");
return trace;
}
-
-void JfrStackFrame::resolve_lineno() const {
- assert(_method, "no method pointer");
- assert(_line == 0, "already have linenumber");
- _line = _method->line_number_from_bci(_bci);
- _method = NULL;
-}
-
-void JfrStackTrace::set_frame(u4 frame_pos, JfrStackFrame& frame) {
- assert(frame_pos < _max_frames, "illegal frame_pos");
- _frames[frame_pos] = frame;
-}
-
-void JfrStackTrace::resolve_linenos() const {
- for(unsigned int i = 0; i < _nr_of_frames; i++) {
- _frames[i].resolve_lineno();
- }
- _lineno = true;
-}
-
-bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp /* false */) {
- assert(thread == Thread::current(), "Thread stack needs to be walkable");
- vframeStream vfs(thread);
- u4 count = 0;
- _reached_root = true;
- for(int i = 0; i < skip; i++) {
- if (vfs.at_end()) {
- break;
- }
- vfs.next();
- }
-
- while (!vfs.at_end()) {
- if (count >= _max_frames) {
- _reached_root = false;
- break;
- }
- const Method* method = vfs.method();
- const traceid mid = JfrTraceId::use(method, leakp);
- int type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
- int bci = 0;
- if (method->is_native()) {
- type = JfrStackFrame::FRAME_NATIVE;
- } else {
- bci = vfs.bci();
- }
- // Can we determine if it's inlined?
- _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
- _frames[count] = JfrStackFrame(mid, bci, type, method);
- vfs.next();
- count++;
- }
-
- _nr_of_frames = count;
- return true;
-}
-
-bool JfrStackTrace::record_thread(JavaThread& thread, frame& frame) {
- vframeStreamSamples st(&thread, frame, false);
- u4 count = 0;
- _reached_root = true;
-
- while (!st.at_end()) {
- if (count >= _max_frames) {
- _reached_root = false;
- break;
- }
- const Method* method = st.method();
- if (!Method::is_valid_method(method)) {
- // we throw away everything we've gathered in this sample since
- // none of it is safe
- return false;
- }
- const traceid mid = JfrTraceId::use(method);
- int type = st.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
- int bci = 0;
- if (method->is_native()) {
- type = JfrStackFrame::FRAME_NATIVE;
- } else {
- bci = st.bci();
- }
- const int lineno = method->line_number_from_bci(bci);
- // Can we determine if it's inlined?
- _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
- _frames[count] = JfrStackFrame(mid, bci, type, lineno);
- st.samples_next();
- count++;
- }
-
- _lineno = true;
- _nr_of_frames = count;
- return true;
-}
-
-void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
- JfrFrameType fct;
- writer.write_type(TYPE_FRAMETYPE);
- fct.serialize(writer);
-}
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,133 +25,49 @@
#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP
#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP
+#include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
#include "jfr/utilities/jfrAllocation.hpp"
#include "jfr/utilities/jfrTypes.hpp"
-class frame;
class JavaThread;
class JfrCheckpointWriter;
class JfrChunkWriter;
-class Method;
-
-class JfrStackFrame {
- private:
- mutable const Method* _method;
- traceid _methodid;
- mutable int _line;
- int _bci;
- u1 _type;
-
- public:
- enum {
- FRAME_INTERPRETER = 0,
- FRAME_JIT,
- FRAME_INLINE,
- FRAME_NATIVE,
- NUM_FRAME_TYPES
- };
-
- JfrStackFrame(const traceid& id, int bci, int type, const Method* method) :
- _method(method), _methodid(id), _line(0), _bci(bci), _type(type) {}
- JfrStackFrame(const traceid& id, int bci, int type, int lineno) :
- _method(NULL), _methodid(id), _line(lineno), _bci(bci), _type(type) {}
- bool equals(const JfrStackFrame& rhs) const;
- void write(JfrChunkWriter& cw) const;
- void write(JfrCheckpointWriter& cpw) const;
- void resolve_lineno() const;
-};
-
-class JfrStackTrace : public StackObj {
- friend class JfrStackTraceRepository;
- private:
- JfrStackFrame* _frames;
- traceid _id;
- u4 _nr_of_frames;
- unsigned int _hash;
- const u4 _max_frames;
- bool _reached_root;
- mutable bool _lineno;
-
- public:
- JfrStackTrace(JfrStackFrame* frames, u4 max_frames) : _frames(frames),
- _id(0),
- _nr_of_frames(0),
- _hash(0),
- _max_frames(max_frames),
- _reached_root(false),
- _lineno(false) {}
- bool record_thread(JavaThread& thread, frame& frame);
- bool record_safe(JavaThread* thread, int skip, bool leakp = false);
- void resolve_linenos() const;
- void set_nr_of_frames(u4 nr_of_frames) { _nr_of_frames = nr_of_frames; }
- void set_hash(unsigned int hash) { _hash = hash; }
- unsigned int hash() const { return _hash; }
- void set_frame(u4 frame_pos, JfrStackFrame& frame);
- void set_reached_root(bool reached_root) { _reached_root = reached_root; }
- bool full_stacktrace() const { return _reached_root; }
- bool have_lineno() const { return _lineno; }
-};
class JfrStackTraceRepository : public JfrCHeapObj {
friend class JfrRecorder;
friend class JfrRecorderService;
+ friend class JfrThreadSampleClosure;
+ friend class ObjectSampleCheckpoint;
friend class ObjectSampler;
- friend class WriteObjectSampleStacktrace;
-
- class StackTrace : public JfrCHeapObj {
- friend class JfrStackTrace;
- friend class JfrStackTraceRepository;
- private:
- StackTrace* _next;
- JfrStackFrame* _frames;
- const traceid _id;
- u4 _nr_of_frames;
- unsigned int _hash;
- bool _reached_root;
- mutable bool _written;
-
- unsigned int hash() const { return _hash; }
- bool should_write() const { return !_written; }
-
- public:
- StackTrace(traceid id, const JfrStackTrace& trace, StackTrace* next);
- ~StackTrace();
- traceid id() const { return _id; }
- StackTrace* next() const { return _next; }
- void write(JfrChunkWriter& cw) const;
- void write(JfrCheckpointWriter& cpw) const;
- bool equals(const JfrStackTrace& rhs) const;
- };
+ friend class StackTraceBlobInstaller;
+ friend class WriteStackTraceRepository;
private:
static const u4 TABLE_SIZE = 2053;
- StackTrace* _table[TABLE_SIZE];
+ JfrStackTrace* _table[TABLE_SIZE];
traceid _next_id;
u4 _entries;
- traceid add_trace(const JfrStackTrace& stacktrace);
- static traceid add(const JfrStackTrace* stacktrace, JavaThread* thread);
- traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
-
- size_t write_impl(JfrChunkWriter& cw, bool clear);
- const StackTrace* resolve_entry(unsigned int hash, traceid id) const;
- static void write_metadata(JfrCheckpointWriter& cpw);
-
- static bool fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip);
-
JfrStackTraceRepository();
+ static JfrStackTraceRepository& instance();
static JfrStackTraceRepository* create();
bool initialize();
static void destroy();
- static JfrStackTraceRepository& instance();
-
- public:
- static traceid add(const JfrStackTrace& stacktrace);
- static traceid record(Thread* thread, int skip = 0);
+ size_t write_impl(JfrChunkWriter& cw, bool clear);
+ static void write_metadata(JfrCheckpointWriter& cpw);
traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash);
size_t write(JfrChunkWriter& cw, bool clear);
size_t clear();
+
+ traceid add_trace(const JfrStackTrace& stacktrace);
+ static traceid add(const JfrStackTrace& stacktrace);
+ traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
+ const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
+
+ public:
+ static traceid record(Thread* thread, int skip = 0);
+ static void record_and_cache(JavaThread* thread, int skip = 0);
};
#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -27,10 +27,6 @@
#include "jfr/utilities/jfrAllocation.hpp"
#include "jfr/utilities/jfrDoublyLinkedList.hpp"
#include "jfr/utilities/jfrIterator.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
-#include "runtime/os.hpp"
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
template <typename T, template <typename> class RetrievalType, typename Callback>
class JfrMemorySpace : public JfrCHeapObj {
@@ -107,62 +103,4 @@
debug_only(bool in_free_list(const Type* t) const { return _free.in_list(t); })
};
-// allocations are even multiples of the mspace min size
-inline u8 align_allocation_size(u8 requested_size, size_t min_elem_size) {
- assert((int)min_elem_size % os::vm_page_size() == 0, "invariant");
- u8 alloc_size_bytes = min_elem_size;
- while (requested_size > alloc_size_bytes) {
- alloc_size_bytes <<= 1;
- }
- assert((int)alloc_size_bytes % os::vm_page_size() == 0, "invariant");
- return alloc_size_bytes;
-}
-
-template <typename T, template <typename> class RetrievalType, typename Callback>
-T* JfrMemorySpace<T, RetrievalType, Callback>::allocate(size_t size) {
- const u8 aligned_size_bytes = align_allocation_size(size, _min_elem_size);
- void* const allocation = JfrCHeapObj::new_array<u1>(aligned_size_bytes + sizeof(T));
- if (allocation == NULL) {
- return NULL;
- }
- T* const t = new (allocation) T;
- assert(t != NULL, "invariant");
- if (!t->initialize(sizeof(T), aligned_size_bytes)) {
- JfrCHeapObj::free(t, aligned_size_bytes + sizeof(T));
- return NULL;
- }
- return t;
-}
-
-template <typename T, template <typename> class RetrievalType, typename Callback>
-void JfrMemorySpace<T, RetrievalType, Callback>::deallocate(T* t) {
- assert(t != NULL, "invariant");
- assert(!_free.in_list(t), "invariant");
- assert(!_full.in_list(t), "invariant");
- assert(t != NULL, "invariant");
- JfrCHeapObj::free(t, t->total_size());
-}
-
-template <typename Mspace>
-class MspaceLock {
- private:
- Mspace* _mspace;
- public:
- MspaceLock(Mspace* mspace) : _mspace(mspace) { _mspace->lock(); }
- ~MspaceLock() { _mspace->unlock(); }
-};
-
-template <typename Mspace>
-class ReleaseOp : public StackObj {
- private:
- Mspace* _mspace;
- Thread* _thread;
- bool _release_full;
- public:
- typedef typename Mspace::Type Type;
- ReleaseOp(Mspace* mspace, Thread* thread, bool release_full = true) : _mspace(mspace), _thread(thread), _release_full(release_full) {}
- bool process(Type* t);
- size_t processed() const { return 0; }
-};
-
#endif // SHARE_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,6 +26,7 @@
#define SHARE_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_INLINE_HPP
#include "jfr/recorder/storage/jfrMemorySpace.hpp"
+#include "runtime/os.hpp"
template <typename T, template <typename> class RetrievalType, typename Callback>
JfrMemorySpace<T, RetrievalType, Callback>::
@@ -69,6 +70,42 @@
return true;
}
+// allocations are even multiples of the mspace min size
+static inline size_t align_allocation_size(size_t requested_size, size_t min_elem_size) {
+ assert((int)min_elem_size % os::vm_page_size() == 0, "invariant");
+ u8 alloc_size_bytes = min_elem_size;
+ while (requested_size > alloc_size_bytes) {
+ alloc_size_bytes <<= 1;
+ }
+ assert((int)alloc_size_bytes % os::vm_page_size() == 0, "invariant");
+ return (size_t)alloc_size_bytes;
+}
+
+template <typename T, template <typename> class RetrievalType, typename Callback>
+inline T* JfrMemorySpace<T, RetrievalType, Callback>::allocate(size_t size) {
+ const size_t aligned_size_bytes = align_allocation_size(size, _min_elem_size);
+ void* const allocation = JfrCHeapObj::new_array<u1>(aligned_size_bytes + sizeof(T));
+ if (allocation == NULL) {
+ return NULL;
+ }
+ T* const t = new (allocation) T;
+ assert(t != NULL, "invariant");
+ if (!t->initialize(sizeof(T), aligned_size_bytes)) {
+ JfrCHeapObj::free(t, aligned_size_bytes + sizeof(T));
+ return NULL;
+ }
+ return t;
+}
+
+template <typename T, template <typename> class RetrievalType, typename Callback>
+inline void JfrMemorySpace<T, RetrievalType, Callback>::deallocate(T* t) {
+ assert(t != NULL, "invariant");
+ assert(!_free.in_list(t), "invariant");
+ assert(!_full.in_list(t), "invariant");
+ assert(t != NULL, "invariant");
+ JfrCHeapObj::free(t, t->total_size());
+}
+
template <typename T, template <typename> class RetrievalType, typename Callback>
inline void JfrMemorySpace<T, RetrievalType, Callback>::release_full(T* t) {
assert(is_locked(), "invariant");
@@ -122,6 +159,15 @@
}
}
+template <typename Mspace, typename Callback>
+static inline Mspace* create_mspace(size_t buffer_size, size_t limit, size_t cache_count, Callback* cb) {
+ Mspace* const mspace = new Mspace(buffer_size, limit, cache_count, cb);
+ if (mspace != NULL) {
+ mspace->initialize();
+ }
+ return mspace;
+}
+
template <typename Mspace>
inline size_t size_adjustment(size_t size, Mspace* mspace) {
assert(mspace != NULL, "invariant");
@@ -174,6 +220,15 @@
}
template <typename Mspace>
+class MspaceLock {
+ private:
+ Mspace* _mspace;
+ public:
+ MspaceLock(Mspace* mspace) : _mspace(mspace) { _mspace->lock(); }
+ ~MspaceLock() { _mspace->unlock(); }
+};
+
+template <typename Mspace>
inline typename Mspace::Type* mspace_allocate_transient_to_full(size_t size, Mspace* mspace, Thread* thread) {
typename Mspace::Type* const t = mspace_allocate_transient(size, mspace, thread);
if (t == NULL) return NULL;
@@ -344,6 +399,20 @@
}
template <typename Mspace>
+class ReleaseOp : public StackObj {
+ private:
+ Mspace* _mspace;
+ Thread* _thread;
+ bool _release_full;
+ public:
+ typedef typename Mspace::Type Type;
+ ReleaseOp(Mspace* mspace, Thread* thread, bool release_full = true) :
+ _mspace(mspace), _thread(thread), _release_full(release_full) {}
+ bool process(Type* t);
+ size_t processed() const { return 0; }
+};
+
+template <typename Mspace>
inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
assert(t != NULL, "invariant");
// assumes some means of exclusive access to t
--- a/src/hotspot/share/jfr/support/jfrKlassExtension.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrKlassExtension.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -36,6 +36,7 @@
#define JDK_JFR_EVENT_SUBKLASS 16
#define JDK_JFR_EVENT_KLASS 32
#define EVENT_HOST_KLASS 64
+#define EVENT_RESERVED 128
#define IS_EVENT_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)) != 0)
#define ON_KLASS_CREATION(k, p, t) if (IS_EVENT_KLASS(k)) JfrEventClassTransformer::on_klass_creation(k, p, t)
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,13 +25,13 @@
#include "precompiled.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
+#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/storage/jfrStorage.hpp"
-#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/os.hpp"
@@ -46,7 +46,7 @@
_shelved_buffer(NULL),
_stackframes(NULL),
_trace_id(JfrTraceId::assign_thread_id()),
- _thread_cp(),
+ _thread(),
_data_lost(0),
_stack_trace_id(max_julong),
_user_time(0),
@@ -62,17 +62,17 @@
return _data_lost;
}
-bool JfrThreadLocal::has_thread_checkpoint() const {
- return _thread_cp.valid();
+bool JfrThreadLocal::has_thread_blob() const {
+ return _thread.valid();
}
-void JfrThreadLocal::set_thread_checkpoint(const JfrCheckpointBlobHandle& ref) {
- assert(!_thread_cp.valid(), "invariant");
- _thread_cp = ref;
+void JfrThreadLocal::set_thread_blob(const JfrBlobHandle& ref) {
+ assert(!_thread.valid(), "invariant");
+ _thread = ref;
}
-const JfrCheckpointBlobHandle& JfrThreadLocal::thread_checkpoint() const {
- return _thread_cp;
+const JfrBlobHandle& JfrThreadLocal::thread_blob() const {
+ return _thread;
}
static void send_java_thread_start_event(JavaThread* jt) {
@@ -95,10 +95,12 @@
assert(jt != NULL, "invariant");
assert(Thread::current() == jt, "invariant");
assert(jt->jfr_thread_local()->trace_id() == id, "invariant");
- EventThreadEnd event;
- event.set_thread(id);
- event.commit();
- JfrThreadCPULoadEvent::send_event_for_thread(jt);
+ if (JfrRecorder::is_recording()) {
+ EventThreadEnd event;
+ event.set_thread(id);
+ event.commit();
+ JfrThreadCPULoadEvent::send_event_for_thread(jt);
+ }
}
void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) {
@@ -125,10 +127,10 @@
assert(t != NULL, "invariant");
JfrThreadLocal * const tl = t->jfr_thread_local();
assert(!tl->is_dead(), "invariant");
- if (JfrRecorder::is_recording()) {
- if (t->is_Java_thread()) {
- send_java_thread_end_events(tl->thread_id(), (JavaThread*)t);
- }
+ if (t->is_Java_thread()) {
+ JavaThread* const jt = (JavaThread*)t;
+ ObjectSampleCheckpoint::on_thread_exit(jt);
+ send_java_thread_end_events(tl->thread_id(), jt);
}
release(tl, Thread::current()); // because it could be that Thread::current() != t
}
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,7 +25,7 @@
#ifndef SHARE_JFR_SUPPORT_JFRTHREADLOCAL_HPP
#define SHARE_JFR_SUPPORT_JFRTHREADLOCAL_HPP
-#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp"
+#include "jfr/utilities/jfrBlob.hpp"
#include "jfr/utilities/jfrTypes.hpp"
class JavaThread;
@@ -41,7 +41,7 @@
JfrBuffer* _shelved_buffer;
mutable JfrStackFrame* _stackframes;
mutable traceid _trace_id;
- JfrCheckpointBlobHandle _thread_cp;
+ JfrBlobHandle _thread;
u8 _data_lost;
traceid _stack_trace_id;
jlong _user_time;
@@ -207,9 +207,9 @@
return _dead;
}
- bool has_thread_checkpoint() const;
- void set_thread_checkpoint(const JfrCheckpointBlobHandle& handle);
- const JfrCheckpointBlobHandle& thread_checkpoint() const;
+ bool has_thread_blob() const;
+ void set_thread_blob(const JfrBlobHandle& handle);
+ const JfrBlobHandle& thread_blob() const;
static void on_start(Thread* t);
static void on_exit(Thread* t);
--- a/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,6 +26,7 @@
#define SHARE_JFR_SUPPORT_JFRTRACEIDEXTENSION_HPP
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
+#include "utilities/macros.hpp"
#define DEFINE_TRACE_ID_FIELD mutable traceid _trace_id
@@ -43,39 +44,55 @@
class JfrTraceFlag {
private:
- mutable jbyte _flags;
+ mutable jshort _flags;
public:
JfrTraceFlag() : _flags(0) {}
- explicit JfrTraceFlag(jbyte flags) : _flags(flags) {}
- void set_flag(jbyte flag) const {
- _flags |= flag;
- }
- void clear_flag(jbyte flag) const {
- _flags &= (~flag);
- }
- jbyte flags() const { return _flags; }
- bool is_set(jbyte flag) const {
+ bool is_set(jshort flag) const {
return (_flags & flag) != 0;
}
- jbyte* const flags_addr() const {
- return &_flags;
+
+ jshort flags() const {
+ return _flags;
+ }
+
+ void set_flags(jshort flags) const {
+ _flags = flags;
+ }
+
+ jbyte* flags_addr() const {
+#ifdef VM_LITTLE_ENDIAN
+ return (jbyte*)&_flags;
+#else
+ return ((jbyte*)&_flags) + 1;
+#endif
+ }
+
+ jbyte* meta_addr() const {
+#ifdef VM_LITTLE_ENDIAN
+ return (jbyte*)(&_flags) + 1;
+#else
+ return (jbyte*)&_flags;
+#endif
}
};
#define DEFINE_TRACE_FLAG mutable JfrTraceFlag _trace_flags
#define DEFINE_TRACE_FLAG_ACCESSOR \
- void set_trace_flag(jbyte flag) const { \
- _trace_flags.set_flag(flag); \
+ bool is_trace_flag_set(jshort flag) const { \
+ return _trace_flags.is_set(flag); \
} \
- jbyte trace_flags() const { \
+ jshort trace_flags() const { \
return _trace_flags.flags(); \
} \
- bool is_trace_flag_set(jbyte flag) const { \
- return _trace_flags.is_set(flag); \
+ void set_trace_flags(jshort flags) const { \
+ _trace_flags.set_flags(flags); \
} \
- jbyte* const trace_flags_addr() const { \
+ jbyte* trace_flags_addr() const { \
return _trace_flags.flags_addr(); \
+ } \
+ jbyte* trace_meta_addr() const { \
+ return _trace_flags.meta_addr(); \
}
#endif // SHARE_JFR_SUPPORT_JFRTRACEIDEXTENSION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrBlob.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/utilities/jfrBlob.hpp"
+
+JfrBlob::JfrBlob(const u1* checkpoint, size_t size) :
+ _data(JfrCHeapObj::new_array<u1>(size)),
+ _next(),
+ _size(size),
+ _written(false) {
+ assert(_data != NULL, "invariant");
+ memcpy(const_cast<u1*>(_data), checkpoint, size);
+}
+
+JfrBlob::~JfrBlob() {
+ JfrCHeapObj::free(const_cast<u1*>(_data), _size);
+}
+
+void JfrBlob::reset_write_state() const {
+ if (!_written) {
+ return;
+ }
+ _written = false;
+ if (_next.valid()) {
+ _next->reset_write_state();
+ }
+}
+
+void JfrBlob::set_next(const JfrBlobHandle& ref) {
+ if (_next == ref) {
+ return;
+ }
+ assert(_next != ref, "invariant");
+ if (_next.valid()) {
+ _next->set_next(ref);
+ return;
+ }
+ _next = ref;
+}
+
+JfrBlobHandle JfrBlob::make(const u1* data, size_t size) {
+ const JfrBlob* const blob = new JfrBlob(data, size);
+ assert(blob != NULL, "invariant");
+ return JfrBlobReference::make(blob);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrBlob.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef SHARE_JFR_UTILITIES_JFRBLOB_HPP
+#define SHARE_JFR_UTILITIES_JFRBLOB_HPP
+
+#include "jfr/utilities/jfrAllocation.hpp"
+#include "jfr/utilities/jfrRefCountPointer.hpp"
+
+class JfrBlob;
+typedef RefCountPointer<JfrBlob, MultiThreadedRefCounter> JfrBlobReference;
+typedef RefCountHandle<JfrBlobReference> JfrBlobHandle;
+
+class JfrBlob : public JfrCHeapObj {
+ template <typename, typename>
+ friend class RefCountPointer;
+ private:
+ const u1* const _data;
+ JfrBlobHandle _next;
+ const size_t _size;
+ mutable bool _written;
+
+ JfrBlob(const u1* data, size_t size);
+ ~JfrBlob();
+
+ public:
+ void set_next(const JfrBlobHandle& ref);
+ void reset_write_state() const;
+ static JfrBlobHandle make(const u1* data, size_t size);
+ template <typename Writer>
+ void write(Writer& writer) const {
+ writer.bytes(_data, _size);
+ if (_next.valid()) {
+ _next->write(writer);
+ }
+ }
+ template <typename Writer>
+ void exclusive_write(Writer& writer) const {
+ if (_written) {
+ return;
+ }
+ writer.bytes(_data, _size);
+ _written = true;
+ if (_next.valid()) {
+ _next->exclusive_write(writer);
+ }
+ }
+};
+
+#endif // SHARE_JFR_UTILITIES_JFRBLOB_HPP
--- a/src/hotspot/share/jfr/utilities/jfrHashtable.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrHashtable.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -25,13 +25,13 @@
#ifndef SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
#define SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
-#include "memory/allocation.inline.hpp"
+#include "jfr/utilities/jfrAllocation.hpp"
#include "runtime/orderAccess.hpp"
#include "utilities/debug.hpp"
#include "utilities/macros.hpp"
template <typename T>
-class JfrBasicHashtableEntry {
+class JfrBasicHashtableEntry : public JfrCHeapObj {
private:
typedef JfrBasicHashtableEntry<T> Entry;
Entry* _next;
@@ -39,8 +39,8 @@
uintptr_t _hash;
public:
+ JfrBasicHashtableEntry(uintptr_t hash, const T& data) : _next(NULL), _literal(data), _hash(hash) {}
uintptr_t hash() const { return _hash; }
- void set_hash(uintptr_t hash) { _hash = hash; }
T literal() const { return _literal; }
T* literal_addr() { return &_literal; }
void set_literal(T s) { _literal = s; }
@@ -93,7 +93,6 @@
}
void free_buckets() {
FREE_C_HEAP_ARRAY(Bucket, _buckets);
- _buckets = NULL;
}
TableEntry* bucket(size_t i) { return _buckets[i].get_entry();}
TableEntry** bucket_addr(size_t i) { return _buckets[i].entry_addr(); }
@@ -108,18 +107,18 @@
};
template <typename IdType, typename Entry, typename T>
-class AscendingId : public CHeapObj<mtTracing> {
+class AscendingId : public JfrCHeapObj {
private:
IdType _id;
public:
AscendingId() : _id(0) {}
// callbacks
- void assign_id(Entry* entry) {
+ void on_link(Entry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_id);
}
- bool equals(const T& data, uintptr_t hash, const Entry* entry) {
+ bool on_equals(uintptr_t hash, const Entry* entry) {
assert(entry->hash() == hash, "invariant");
return true;
}
@@ -127,18 +126,16 @@
// IdType must be scalar
template <typename T, typename IdType>
-class Entry : public JfrBasicHashtableEntry<T> {
+class JfrHashtableEntry : public JfrBasicHashtableEntry<T> {
public:
+ JfrHashtableEntry(uintptr_t hash, const T& data) : JfrBasicHashtableEntry<T>(hash, data), _id(0) {}
typedef IdType ID;
- void init() { _id = 0; }
ID id() const { return _id; }
- void set_id(ID id) { _id = id; }
- void set_value(const T& value) { this->set_literal(value); }
- T& value() const { return *const_cast<Entry*>(this)->literal_addr();}
- const T* value_addr() const { return const_cast<Entry*>(this)->literal_addr(); }
-
+ void set_id(ID id) const { _id = id; }
+ T& value() const { return *const_cast<JfrHashtableEntry*>(this)->literal_addr();}
+ const T* value_addr() const { return const_cast<JfrHashtableEntry*>(this)->literal_addr(); }
private:
- ID _id;
+ mutable ID _id;
};
template <typename T, typename IdType, template <typename, typename> class Entry,
@@ -147,29 +144,28 @@
class HashTableHost : public JfrBasicHashtable<T> {
public:
typedef Entry<T, IdType> HashEntry;
- HashTableHost() : _callback(new Callback()) {}
- HashTableHost(Callback* cb) : JfrBasicHashtable<T>(TABLE_SIZE, sizeof(HashEntry)), _callback(cb) {}
+ HashTableHost(size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(new Callback()) {}
+ HashTableHost(Callback* cb, size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(cb) {}
~HashTableHost() {
this->clear_entries();
this->free_buckets();
}
// direct insert assumes non-existing entry
- HashEntry& put(const T& data, uintptr_t hash);
+ HashEntry& put(uintptr_t hash, const T& data);
// lookup entry, will put if not found
- HashEntry& lookup_put(const T& data, uintptr_t hash) {
- HashEntry* entry = lookup_only(data, hash);
- return entry == NULL ? put(data, hash) : *entry;
+ HashEntry& lookup_put(uintptr_t hash, const T& data) {
+ HashEntry* entry = lookup_only(hash);
+ return entry == NULL ? put(hash, data) : *entry;
}
- // read-only lookup
- HashEntry* lookup_only(const T& query, uintptr_t hash);
+ HashEntry* lookup_only(uintptr_t hash);
// id retrieval
- IdType id(const T& data, uintptr_t hash) {
+ IdType id(uintptr_t hash, const T& data) {
assert(data != NULL, "invariant");
- const HashEntry& entry = lookup_put(data, hash);
+ const HashEntry& entry = lookup_put(hash, data);
assert(entry.id() > 0, "invariant");
return entry.id();
}
@@ -188,34 +184,35 @@
void free_entry(HashEntry* entry) {
assert(entry != NULL, "invariant");
JfrBasicHashtable<T>::unlink_entry(entry);
- FREE_C_HEAP_ARRAY(char, entry);
+ _callback->on_unlink(entry);
+ delete entry;
}
private:
Callback* _callback;
size_t index_for(uintptr_t hash) { return this->hash_to_index(hash); }
- HashEntry* new_entry(const T& data, uintptr_t hash);
+ HashEntry* new_entry(uintptr_t hash, const T& data);
void add_entry(size_t index, HashEntry* new_entry) {
assert(new_entry != NULL, "invariant");
- _callback->assign_id(new_entry);
+ _callback->on_link(new_entry);
assert(new_entry->id() > 0, "invariant");
JfrBasicHashtable<T>::add_entry(index, new_entry);
}
};
template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
-Entry<T, IdType>& HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::put(const T& data, uintptr_t hash) {
- assert(lookup_only(data, hash) == NULL, "use lookup_put()");
- HashEntry* const entry = new_entry(data, hash);
+Entry<T, IdType>& HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::put(uintptr_t hash, const T& data) {
+ assert(lookup_only(hash) == NULL, "use lookup_put()");
+ HashEntry* const entry = new_entry(hash, data);
add_entry(index_for(hash), entry);
return *entry;
}
template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
-Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::lookup_only(const T& query, uintptr_t hash) {
+Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::lookup_only(uintptr_t hash) {
HashEntry* entry = (HashEntry*)this->bucket(index_for(hash));
while (entry != NULL) {
- if (entry->hash() == hash && _callback->equals(query, hash, entry)) {
+ if (entry->hash() == hash && _callback->on_equals(hash, entry)) {
return entry;
}
entry = (HashEntry*)entry->next();
@@ -267,13 +264,10 @@
}
template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
-Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::new_entry(const T& data, uintptr_t hash) {
+Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::new_entry(uintptr_t hash, const T& data) {
assert(sizeof(HashEntry) == this->entry_size(), "invariant");
- HashEntry* const entry = (HashEntry*) NEW_C_HEAP_ARRAY2(char, this->entry_size(), mtTracing, CURRENT_PC);
- entry->init();
- entry->set_hash(hash);
- entry->set_value(data);
- entry->set_next(NULL);
+ HashEntry* const entry = new HashEntry(hash, data);
+ assert(entry != NULL, "invariant");
assert(0 == entry->id(), "invariant");
return entry;
}
--- a/src/hotspot/share/jfr/utilities/jfrTypes.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrTypes.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -26,8 +26,6 @@
#define SHARE_JFR_UTILITIES_JFRTYPES_HPP
#include "jfrfiles/jfrEventIds.hpp"
-#include "memory/allocation.hpp"
-#include "utilities/globalDefinitions.hpp"
typedef u8 traceid;
typedef int fio_fd;
@@ -37,6 +35,14 @@
const u4 MIN_STACK_DEPTH = 1;
const u4 MAX_STACK_DEPTH = 2048;
+inline int compare_traceid(const traceid& lhs, const traceid& rhs) {
+ return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0;
+}
+
+inline int sort_traceid(traceid* lhs, traceid* rhs) {
+ return compare_traceid(*lhs, *rhs);
+}
+
enum EventStartTime {
UNTIMED,
TIMED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/writers/jfrTypeWriterHost.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef SHARE_JFR_WRITERS_JFRTYPEWRITERHOST_HPP
+#define SHARE_JFR_WRITERS_JFRTYPEWRITERHOST_HPP
+
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+#include "memory/allocation.hpp"
+
+template <typename WriterImpl, u4 ID>
+class JfrTypeWriterHost : public StackObj {
+ private:
+ WriterImpl _impl;
+ JfrCheckpointWriter* _writer;
+ JfrCheckpointContext _ctx;
+ int64_t _count_offset;
+ int _count;
+ bool _skip_header;
+ public:
+ JfrTypeWriterHost(JfrCheckpointWriter* writer,
+ bool class_unload = false,
+ bool skip_header = false) : _impl(writer, class_unload),
+ _writer(writer),
+ _ctx(writer->context()),
+ _count(0),
+ _skip_header(skip_header) {
+ assert(_writer != NULL, "invariant");
+ if (!_skip_header) {
+ _writer->write_type((JfrTypeId)ID);
+ _count_offset = _writer->reserve(sizeof(u4)); // Don't know how many yet
+ }
+ }
+
+ ~JfrTypeWriterHost() {
+ if (_count == 0) {
+ // nothing written, restore context for rewind
+ _writer->set_context(_ctx);
+ return;
+ }
+ assert(_count > 0, "invariant");
+ if (!_skip_header) {
+ _writer->write_count(_count, _count_offset);
+ }
+ }
+
+ bool operator()(typename WriterImpl::Type const & value) {
+ this->_count += _impl(value);
+ return true;
+ }
+
+ int count() const { return _count; }
+ void add(int count) { _count += count; }
+};
+
+typedef int(*type_write_operation)(JfrCheckpointWriter*, const void*);
+
+template <typename T, type_write_operation op>
+class JfrTypeWriterImplHost {
+ private:
+ JfrCheckpointWriter* _writer;
+ public:
+ typedef T Type;
+ JfrTypeWriterImplHost(JfrCheckpointWriter* writer, bool class_unload = false) : _writer(writer) {}
+ int operator()(T const& value) {
+ return op(this->_writer, value);
+ }
+};
+
+template <typename T, typename Predicate, type_write_operation op>
+class JfrPredicatedTypeWriterImplHost : public JfrTypeWriterImplHost<T, op> {
+ private:
+ Predicate _predicate;
+ typedef JfrTypeWriterImplHost<T, op> Parent;
+ public:
+ JfrPredicatedTypeWriterImplHost(JfrCheckpointWriter* writer, bool class_unload = false) :
+ Parent(writer), _predicate(class_unload) {}
+ int operator()(T const& value) {
+ return _predicate(value) ? Parent::operator()(value) : 0;
+ }
+};
+
+#endif // SHARE_JFR_WRITERS_JFRTYPEWRITERHOST_HPP
--- a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -170,7 +170,7 @@
}
if (this->available_size() < requested + size_safety_cushion) {
if (!this->accommodate(this->used_size(), requested + size_safety_cushion)) {
- this->cancel();
+ assert(!this->is_valid(), "invariant");
return NULL;
}
}
--- a/src/hotspot/share/jvmci/jvmciCompiler.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -123,7 +123,7 @@
if (excludeModules.not_null()) {
ModuleEntry* moduleEntry = method->method_holder()->module();
for (int i = 0; i < excludeModules->length(); i++) {
- if (oopDesc::equals(excludeModules->obj_at(i), moduleEntry->module())) {
+ if (excludeModules->obj_at(i) == moduleEntry->module()) {
return true;
}
}
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -631,7 +631,7 @@
// The other thread may exit during this process, which is ok so return false.
return JNI_FALSE;
} else {
- return (jint) Thread::is_interrupted(receiverThread, clear_interrupted != 0);
+ return (jint) receiverThread->is_interrupted(clear_interrupted != 0);
}
JRT_END
--- a/src/hotspot/share/memory/filemap.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/memory/filemap.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -460,7 +460,7 @@
if (e->is_dir()) {
const char* path = e->name();
if (!os::dir_is_empty(path)) {
- tty->print_cr("Error: non-empty directory '%s'", path);
+ log_error(cds)("Error: non-empty directory '%s'", path);
has_nonempty_dir = true;
}
}
--- a/src/hotspot/share/memory/heapShared.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/memory/heapShared.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -125,7 +125,7 @@
static bool _archive_heap_region_fixed;
static bool oop_equals(oop const& p1, oop const& p2) {
- return oopDesc::equals(p1, p2);
+ return p1 == p2;
}
static unsigned oop_hash(oop const& p);
--- a/src/hotspot/share/memory/heapShared.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/memory/heapShared.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -27,6 +27,7 @@
#include "oops/compressedOops.inline.hpp"
#include "memory/heapShared.hpp"
+#include "utilities/align.hpp"
#if INCLUDE_G1GC
#include "gc/g1/g1Allocator.inline.hpp"
#endif
@@ -40,7 +41,7 @@
inline oop HeapShared::decode_from_archive(narrowOop v) {
assert(!CompressedOops::is_null(v), "narrow oop value can never be zero");
oop result = (oop)(void*)((uintptr_t)_narrow_oop_base + ((uintptr_t)v << _narrow_oop_shift));
- assert(check_obj_alignment(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result));
+ assert(is_object_aligned(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result));
return result;
}
--- a/src/hotspot/share/memory/metaspaceShared.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/memory/metaspaceShared.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1814,7 +1814,7 @@
if (klass == NULL &&
(PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) {
// print a warning only when the pending exception is class not found
- tty->print_cr("Preload Warning: Cannot find %s", parser.current_class_name());
+ log_warning(cds)("Preload Warning: Cannot find %s", parser.current_class_name());
}
CLEAR_PENDING_EXCEPTION;
}
@@ -1860,7 +1860,7 @@
ik->link_class(THREAD);
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm;
- tty->print_cr("Preload Warning: Verification failed for %s",
+ log_warning(cds)("Preload Warning: Verification failed for %s",
ik->external_name());
CLEAR_PENDING_EXCEPTION;
ik->set_in_error_state();
--- a/src/hotspot/share/memory/universe.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/memory/universe.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -571,13 +571,13 @@
// preallocated errors with backtrace have been consumed. Also need to avoid
// a potential loop which could happen if an out of memory occurs when attempting
// to allocate the backtrace.
- return ((!oopDesc::equals(throwable(), Universe::_out_of_memory_error_java_heap)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_metaspace)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_class_metaspace)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_array_size)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_gc_overhead_limit)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_realloc_objects)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_retry)));
+ return ((throwable() != Universe::_out_of_memory_error_java_heap) &&
+ (throwable() != Universe::_out_of_memory_error_metaspace) &&
+ (throwable() != Universe::_out_of_memory_error_class_metaspace) &&
+ (throwable() != Universe::_out_of_memory_error_array_size) &&
+ (throwable() != Universe::_out_of_memory_error_gc_overhead_limit) &&
+ (throwable() != Universe::_out_of_memory_error_realloc_objects) &&
+ (throwable() != Universe::_out_of_memory_error_retry));
}
--- a/src/hotspot/share/oops/access.cpp Thu Sep 12 15:04:00 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2018, 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.
- *
- * 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.
- *
- */
-
-#include "precompiled.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/accessDecorators.hpp"
-
-// This macro allows instantiating selected accesses to be usable from the
-// access.hpp file, to break dependencies to the access.inline.hpp file.
-#define INSTANTIATE_HPP_ACCESS(decorators, T, barrier_type) \
- template struct RuntimeDispatch<DecoratorFixup<decorators>::value, T, barrier_type>
-
-namespace AccessInternal {
- INSTANTIATE_HPP_ACCESS(DECORATORS_NONE, oop, BARRIER_EQUALS);
-}
--- a/src/hotspot/share/oops/access.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/access.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -58,7 +58,6 @@
// * arraycopy: Copy data from one heap array to another heap array. The ArrayAccess class has convenience functions for this.
// * clone: Clone the contents of an object to a newly allocated object.
// * resolve: Resolve a stable to-space invariant oop that is guaranteed not to relocate its payload until a subsequent thread transition.
-// * equals: Object equality, e.g. when different copies of the same objects are in use (from-space vs. to-space)
//
// == IMPLEMENTATION ==
// Each access goes through the following steps in a template pipeline.
@@ -275,11 +274,6 @@
verify_decorators<DECORATORS_NONE>();
return AccessInternal::resolve<decorators>(obj);
}
-
- static bool equals(oop o1, oop o2) {
- verify_decorators<AS_RAW>();
- return AccessInternal::equals<decorators>(o1, o2);
- }
};
// Helper for performing raw accesses (knows only of memory ordering
--- a/src/hotspot/share/oops/access.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/access.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -206,13 +206,6 @@
}
};
- template <class GCBarrierType, DecoratorSet decorators>
- struct PostRuntimeDispatch<GCBarrierType, BARRIER_EQUALS, decorators>: public AllStatic {
- static bool access_barrier(oop o1, oop o2) {
- return GCBarrierType::equals(o1, o2);
- }
- };
-
// Resolving accessors with barriers from the barrier set happens in two steps.
// 1. Expand paths with runtime-decorators, e.g. is UseCompressedOops on or off.
// 2. Expand paths for each BarrierSet available in the system.
@@ -367,13 +360,6 @@
_resolve_func = function;
return function(obj);
}
-
- template <DecoratorSet decorators, typename T>
- bool RuntimeDispatch<decorators, T, BARRIER_EQUALS>::equals_init(oop o1, oop o2) {
- func_t function = BarrierResolver<decorators, func_t, BARRIER_EQUALS>::resolve_barrier();
- _equals_func = function;
- return function(o1, o2);
- }
}
#endif // SHARE_OOPS_ACCESS_INLINE_HPP
--- a/src/hotspot/share/oops/accessBackend.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/accessBackend.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -64,8 +64,7 @@
BARRIER_ATOMIC_XCHG_AT,
BARRIER_ARRAYCOPY,
BARRIER_CLONE,
- BARRIER_RESOLVE,
- BARRIER_EQUALS
+ BARRIER_RESOLVE
};
template <DecoratorSet decorators, typename T>
@@ -116,7 +115,6 @@
size_t length);
typedef void (*clone_func_t)(oop src, oop dst, size_t size);
typedef oop (*resolve_func_t)(oop obj);
- typedef bool (*equals_func_t)(oop o1, oop o2);
};
template <DecoratorSet decorators>
@@ -144,7 +142,6 @@
ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_ARRAYCOPY, arraycopy_func_t);
ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_CLONE, clone_func_t);
ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_RESOLVE, resolve_func_t);
- ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_EQUALS, equals_func_t);
#undef ACCESS_GENERATE_ACCESS_FUNCTION
template <DecoratorSet decorators, typename T, BarrierType barrier_type>
@@ -410,8 +407,6 @@
static void clone(oop src, oop dst, size_t size);
static oop resolve(oop obj) { return obj; }
-
- static bool equals(oop o1, oop o2) { return (void*)o1 == (void*)o2; }
};
// Below is the implementation of the first 4 steps of the template pipeline:
@@ -605,18 +600,6 @@
}
};
- template <DecoratorSet decorators, typename T>
- struct RuntimeDispatch<decorators, T, BARRIER_EQUALS>: AllStatic {
- typedef typename AccessFunction<decorators, T, BARRIER_EQUALS>::type func_t;
- static func_t _equals_func;
-
- static bool equals_init(oop o1, oop o2);
-
- static inline bool equals(oop o1, oop o2) {
- return _equals_func(o1, o2);
- }
- };
-
// Initialize the function pointers to point to the resolving function.
template <DecoratorSet decorators, typename T>
typename AccessFunction<decorators, T, BARRIER_STORE>::type
@@ -662,10 +645,6 @@
typename AccessFunction<decorators, T, BARRIER_RESOLVE>::type
RuntimeDispatch<decorators, T, BARRIER_RESOLVE>::_resolve_func = &resolve_init;
- template <DecoratorSet decorators, typename T>
- typename AccessFunction<decorators, T, BARRIER_EQUALS>::type
- RuntimeDispatch<decorators, T, BARRIER_EQUALS>::_equals_func = &equals_init;
-
// Step 3: Pre-runtime dispatching.
// The PreRuntimeDispatch class is responsible for filtering the barrier strength
// decorators. That is, for AS_RAW, it hardwires the accesses without a runtime
@@ -996,21 +975,6 @@
resolve(oop obj) {
return RuntimeDispatch<decorators, oop, BARRIER_RESOLVE>::resolve(obj);
}
-
- template <DecoratorSet decorators>
- inline static typename EnableIf<
- HasDecorator<decorators, AS_RAW>::value || HasDecorator<decorators, INTERNAL_BT_TO_SPACE_INVARIANT>::value, bool>::type
- equals(oop o1, oop o2) {
- typedef RawAccessBarrier<decorators & RAW_DECORATOR_MASK> Raw;
- return Raw::equals(o1, o2);
- }
-
- template <DecoratorSet decorators>
- inline static typename EnableIf<
- !HasDecorator<decorators, AS_RAW>::value && !HasDecorator<decorators, INTERNAL_BT_TO_SPACE_INVARIANT>::value, bool>::type
- equals(oop o1, oop o2) {
- return RuntimeDispatch<decorators, oop, BARRIER_EQUALS>::equals(o1, o2);
- }
};
// Step 2: Reduce types.
@@ -1309,12 +1273,6 @@
return PreRuntimeDispatch::resolve<expanded_decorators>(obj);
}
- template <DecoratorSet decorators>
- inline bool equals(oop o1, oop o2) {
- const DecoratorSet expanded_decorators = DecoratorFixup<decorators>::value;
- return PreRuntimeDispatch::equals<expanded_decorators>(o1, o2);
- }
-
// Infer the type that should be returned from an Access::oop_load.
template <typename P, DecoratorSet decorators>
class OopLoadProxy: public StackObj {
--- a/src/hotspot/share/oops/compressedOops.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/compressedOops.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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 @@
#include "memory/universe.hpp"
#include "oops/compressedOops.hpp"
#include "oops/oop.hpp"
+#include "utilities/align.hpp"
// Functions for encoding and decoding compressed oops.
// If the oops are compressed, the type passed to these overloaded functions
@@ -47,7 +48,7 @@
inline oop CompressedOops::decode_not_null(narrowOop v) {
assert(!is_null(v), "narrow oop value can never be zero");
oop result = decode_raw(v);
- assert(check_obj_alignment(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result));
+ assert(is_object_aligned(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result));
return result;
}
@@ -62,7 +63,7 @@
assert(OopEncodingHeapMax > pd, "change encoding max if new encoding");
uint64_t result = pd >> shift();
assert((result & CONST64(0xffffffff00000000)) == 0, "narrow oop overflow");
- assert(oopDesc::equals_raw(decode(result), v), "reversibility");
+ assert(decode(result) == v, "reversibility");
return (narrowOop)result;
}
--- a/src/hotspot/share/oops/constantPool.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/constantPool.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -866,7 +866,7 @@
if (cache_index >= 0) {
result_oop = this_cp->resolved_references()->obj_at(cache_index);
if (result_oop != NULL) {
- if (oopDesc::equals(result_oop, Universe::the_null_sentinel())) {
+ if (result_oop == Universe::the_null_sentinel()) {
DEBUG_ONLY(int temp_index = (index >= 0 ? index : this_cp->object_to_cp_index(cache_index)));
assert(this_cp->tag_at(temp_index).is_dynamic_constant(), "only condy uses the null sentinel");
result_oop = NULL;
@@ -1096,12 +1096,12 @@
} else {
// Return the winning thread's result. This can be different than
// the result here for MethodHandles.
- if (oopDesc::equals(old_result, Universe::the_null_sentinel()))
+ if (old_result == Universe::the_null_sentinel())
old_result = NULL;
return old_result;
}
} else {
- assert(!oopDesc::equals(result_oop, Universe::the_null_sentinel()), "");
+ assert(result_oop != Universe::the_null_sentinel(), "");
return result_oop;
}
}
@@ -1154,7 +1154,7 @@
oop ConstantPool::string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS) {
// If the string has already been interned, this entry will be non-null
oop str = this_cp->resolved_references()->obj_at(obj_index);
- assert(!oopDesc::equals(str, Universe::the_null_sentinel()), "");
+ assert(str != Universe::the_null_sentinel(), "");
if (str != NULL) return str;
Symbol* sym = this_cp->unresolved_string_at(which);
str = StringTable::intern(sym, CHECK_(NULL));
--- a/src/hotspot/share/oops/instanceKlass.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/instanceKlass.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -2717,7 +2717,7 @@
// and package entries. Both must be the same. This rule
// applies even to classes that are defined in the unnamed
// package, they still must have the same class loader.
- if (oopDesc::equals(classloader1, classloader2) && (classpkg1 == classpkg2)) {
+ if ((classloader1 == classloader2) && (classpkg1 == classpkg2)) {
return true;
}
@@ -2728,7 +2728,7 @@
// and classname information is enough to determine a class's package
bool InstanceKlass::is_same_class_package(oop other_class_loader,
const Symbol* other_class_name) const {
- if (!oopDesc::equals(class_loader(), other_class_loader)) {
+ if (class_loader() != other_class_loader) {
return false;
}
if (name()->fast_compare(other_class_name) == 0) {
--- a/src/hotspot/share/oops/klassVtable.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/klassVtable.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -491,7 +491,7 @@
// to link to the first super, and we get all the others.
Handle super_loader(THREAD, super_klass->class_loader());
- if (!oopDesc::equals(target_loader(), super_loader())) {
+ if (target_loader() != super_loader()) {
ResourceMark rm(THREAD);
Symbol* failed_type_symbol =
SystemDictionary::check_signature_loaders(signature, target_loader,
@@ -1237,7 +1237,7 @@
// if checkconstraints requested
if (checkconstraints) {
Handle method_holder_loader (THREAD, target->method_holder()->class_loader());
- if (!oopDesc::equals(method_holder_loader(), interface_loader())) {
+ if (method_holder_loader() != interface_loader()) {
ResourceMark rm(THREAD);
Symbol* failed_type_symbol =
SystemDictionary::check_signature_loaders(m->signature(),
--- a/src/hotspot/share/oops/objArrayKlass.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/objArrayKlass.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -212,7 +212,7 @@
// Either oop or narrowOop depending on UseCompressedOops.
void ObjArrayKlass::do_copy(arrayOop s, size_t src_offset,
arrayOop d, size_t dst_offset, int length, TRAPS) {
- if (oopDesc::equals(s, d)) {
+ if (s == d) {
// since source and destination are equal we do not need conversion checks.
assert(length > 0, "sanity check");
ArrayAccess<>::oop_arraycopy(s, src_offset, d, dst_offset, length);
--- a/src/hotspot/share/oops/oop.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/oop.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -150,10 +150,6 @@
}
}
- inline static bool equals(oop o1, oop o2) { return Access<>::equals(o1, o2); }
-
- inline static bool equals_raw(oop o1, oop o2) { return RawAccess<>::equals(o1, o2); }
-
// Access to fields in a instanceOop through these methods.
template <DecoratorSet decorator>
oop obj_field_access(int offset) const;
--- a/src/hotspot/share/oops/oopsHierarchy.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/oopsHierarchy.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -23,9 +23,6 @@
*/
#include "precompiled.hpp"
-#include "gc/shared/barrierSet.hpp"
-#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/collectedHeap.inline.hpp"
#include "memory/universe.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/thread.inline.hpp"
@@ -56,14 +53,4 @@
}
}
-bool oop::operator==(const oop o) const {
- assert(BarrierSet::barrier_set()->oop_equals_operator_allowed(), "Not allowed");
- return obj() == o.obj();
-}
-
-bool oop::operator!=(const volatile oop o) const {
- assert(BarrierSet::barrier_set()->oop_equals_operator_allowed(), "Not allowed");
- return obj() != o.obj();
-}
-
#endif // CHECK_UNHANDLED_OOPS
--- a/src/hotspot/share/oops/oopsHierarchy.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/oops/oopsHierarchy.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -100,9 +100,9 @@
// General access
oopDesc* operator->() const { return obj(); }
- bool operator==(const oop o) const;
+ bool operator==(const oop o) const { return obj() == o.obj(); }
bool operator==(void *p) const { return obj() == p; }
- bool operator!=(const volatile oop o) const;
+ bool operator!=(const volatile oop o) const { return obj() != o.obj(); }
bool operator!=(void *p) const { return obj() != p; }
// Assignment
@@ -190,10 +190,6 @@
return (T)(CHECK_UNHANDLED_OOPS_ONLY((void*))o);
}
-inline bool check_obj_alignment(void* ptr) {
- return (uintptr_t(ptr) & MinObjAlignmentInBytesMask) == 0;
-}
-
// The metadata hierarchy is separate from the oop hierarchy
// class MetaspaceObj
--- a/src/hotspot/share/opto/c2_globals.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/opto/c2_globals.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -510,7 +510,7 @@
"Sets max value cached by the java.lang.Integer autobox cache") \
range(0, max_jint) \
\
- experimental(bool, AggressiveUnboxing, true, \
+ diagnostic(bool, AggressiveUnboxing, true, \
"Control optimizations for aggressive boxing elimination") \
\
develop(bool, TracePostallocExpand, false, "Trace expanding nodes after" \
--- a/src/hotspot/share/prims/jni.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/jni.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -568,7 +568,7 @@
oop super_mirror = JNIHandles::resolve_non_null(super);
if (java_lang_Class::is_primitive(sub_mirror) ||
java_lang_Class::is_primitive(super_mirror)) {
- jboolean ret = oopDesc::equals(sub_mirror, super_mirror);
+ jboolean ret = (sub_mirror == super_mirror);
HOTSPOT_JNI_ISASSIGNABLEFROM_RETURN(ret);
return ret;
--- a/src/hotspot/share/prims/jvm.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/jvm.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1257,7 +1257,7 @@
protection_domain = method->method_holder()->protection_domain();
}
- if ((!oopDesc::equals(previous_protection_domain, protection_domain)) && (protection_domain != NULL)) {
+ if ((previous_protection_domain != protection_domain) && (protection_domain != NULL)) {
local_array->push(protection_domain);
previous_protection_domain = protection_domain;
}
@@ -2974,7 +2974,7 @@
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
- if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
+ if (thread->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
@@ -3071,7 +3071,7 @@
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
if (is_alive) {
// jthread refers to a live JavaThread.
- Thread::interrupt(receiver);
+ receiver->interrupt();
}
JVM_END
@@ -3084,7 +3084,7 @@
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
if (is_alive) {
// jthread refers to a live JavaThread.
- return (jboolean) Thread::is_interrupted(receiver, clear_interrupted != 0);
+ return (jboolean) receiver->is_interrupted(clear_interrupted != 0);
} else {
return JNI_FALSE;
}
--- a/src/hotspot/share/prims/jvmtiEnv.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1101,7 +1101,7 @@
return err;
}
- Thread::interrupt(java_thread);
+ java_thread->interrupt();
return JVMTI_ERROR_NONE;
} /* end InterruptThread */
--- a/src/hotspot/share/prims/jvmtiRawMonitor.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/jvmtiRawMonitor.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -373,8 +373,12 @@
OrderAccess::fence() ;
// check interrupt event
- if (interruptible && Thread::is_interrupted(THREAD, true)) {
- return OM_INTERRUPTED;
+ if (interruptible) {
+ assert(THREAD->is_Java_thread(), "Only JavaThreads can be interruptible");
+ JavaThread* jt = (JavaThread*) THREAD;
+ if (jt->is_interrupted(true)) {
+ return OM_INTERRUPTED;
+ }
}
intptr_t save = _recursions ;
@@ -401,8 +405,11 @@
}
guarantee (THREAD == _owner, "invariant") ;
- if (interruptible && Thread::is_interrupted(THREAD, true)) {
- return OM_INTERRUPTED;
+ if (interruptible) {
+ JavaThread* jt = (JavaThread*) THREAD;
+ if (jt->is_interrupted(true)) {
+ return OM_INTERRUPTED;
+ }
}
return OM_OK ;
}
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -105,7 +105,7 @@
}
inline bool equals(oop object) {
- return oopDesc::equals(object, object_peek());
+ return object == object_peek();
}
inline JvmtiTagHashmapEntry* next() const { return _next; }
--- a/src/hotspot/share/prims/methodHandles.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/methodHandles.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1003,7 +1003,7 @@
if (!java_lang_invoke_MemberName::is_instance(result()))
return -99; // caller bug!
oop saved = MethodHandles::init_field_MemberName(result, st.field_descriptor());
- if (!oopDesc::equals(saved, result()))
+ if (saved != result())
results->obj_at_put(rfill-1, saved); // show saved instance to user
} else if (++overflow >= overflow_limit) {
match_flags = 0; break; // got tired of looking at overflow
@@ -1055,7 +1055,7 @@
return -99; // caller bug!
CallInfo info(m, NULL, CHECK_0);
oop saved = MethodHandles::init_method_MemberName(result, info);
- if (!oopDesc::equals(saved, result()))
+ if (saved != result())
results->obj_at_put(rfill-1, saved); // show saved instance to user
} else if (++overflow >= overflow_limit) {
match_flags = 0; break; // got tired of looking at overflow
--- a/src/hotspot/share/prims/stackwalk.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/stackwalk.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -50,7 +50,7 @@
bool BaseFrameStream::check_magic(objArrayHandle frames_array) {
oop m1 = frames_array->obj_at(magic_pos);
jlong m2 = _anchor;
- if (oopDesc::equals(m1, _thread->threadObj()) && m2 == address_value()) return true;
+ if (m1 == _thread->threadObj() && m2 == address_value()) return true;
return false;
}
@@ -81,7 +81,7 @@
{
assert(thread != NULL && thread->is_Java_thread(), "");
oop m1 = frames_array->obj_at(magic_pos);
- if (!oopDesc::equals(m1, thread->threadObj())) return NULL;
+ if (m1 != thread->threadObj()) return NULL;
if (magic == 0L) return NULL;
BaseFrameStream* stream = (BaseFrameStream*) (intptr_t) magic;
if (!stream->is_valid_in(thread, frames_array)) return NULL;
--- a/src/hotspot/share/prims/unsafe.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/prims/unsafe.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -935,7 +935,7 @@
oop p = JNIHandles::resolve(obj);
assert_field_offset_sane(p, offset);
oop ret = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_atomic_cmpxchg_at(x, p, (ptrdiff_t)offset, e);
- return oopDesc::equals(ret, e);
+ return ret == e;
} UNSAFE_END
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
--- a/src/hotspot/share/runtime/arguments.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/arguments.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -539,6 +539,7 @@
{ "FlightRecorder", JDK_Version::jdk(13), JDK_Version::undefined(), JDK_Version::undefined() },
{ "FieldsAllocationStyle", JDK_Version::jdk(14), JDK_Version::jdk(15), JDK_Version::jdk(16) },
{ "CompactFields", JDK_Version::jdk(14), JDK_Version::jdk(15), JDK_Version::jdk(16) },
+ { "MonitorBound", JDK_Version::jdk(14), JDK_Version::jdk(15), JDK_Version::jdk(16) },
// --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in:
{ "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() },
--- a/src/hotspot/share/runtime/biasedLocking.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/biasedLocking.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -257,7 +257,7 @@
BasicLock* highest_lock = NULL;
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
- if (oopDesc::equals(mon_info->owner(), obj)) {
+ if (mon_info->owner() == obj) {
log_trace(biasedlocking)(" mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")",
p2i((void *) mon_info->owner()),
p2i((void *) obj));
@@ -693,7 +693,7 @@
BasicLock* highest_lock = NULL;
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
- if (oopDesc::equals(mon_info->owner(), obj)) {
+ if (mon_info->owner() == obj) {
log_trace(biasedlocking)(" mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")",
p2i(mon_info->owner()),
p2i(obj));
--- a/src/hotspot/share/runtime/globals.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/globals.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -713,7 +713,7 @@
"Use LWP-based instead of libthread-based synchronization " \
"(SPARC only)") \
\
- product(intx, MonitorBound, 0, "Bound Monitor population") \
+ product(intx, MonitorBound, 0, "(Deprecated) Bound Monitor population") \
range(0, max_jint) \
\
experimental(intx, MonitorUsedDeflationThreshold, 90, \
--- a/src/hotspot/share/runtime/handles.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/handles.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -79,8 +79,8 @@
oop operator () () const { return obj(); }
oop operator -> () const { return non_null_obj(); }
- bool operator == (oop o) const { return oopDesc::equals(obj(), o); }
- bool operator == (const Handle& h) const { return oopDesc::equals(obj(), h.obj()); }
+ bool operator == (oop o) const { return obj() == o; }
+ bool operator == (const Handle& h) const { return obj() == h.obj(); }
// Null checks
bool is_null() const { return _handle == NULL; }
--- a/src/hotspot/share/runtime/jniHandles.inline.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/jniHandles.inline.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -84,7 +84,7 @@
inline bool JNIHandles::is_same_object(jobject handle1, jobject handle2) {
oop obj1 = resolve_no_keepalive(handle1);
oop obj2 = resolve_no_keepalive(handle2);
- return oopDesc::equals(obj1, obj2);
+ return obj1 == obj2;
}
inline oop JNIHandles::resolve_non_null(jobject handle) {
--- a/src/hotspot/share/runtime/objectMonitor.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/objectMonitor.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1205,7 +1205,7 @@
EventJavaMonitorWait event;
// check for a pending interrupt
- if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
+ if (interruptible && jt->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
// post monitor waited event. Note that this is past-tense, we are done waiting.
if (JvmtiExport::should_post_monitor_waited()) {
// Note: 'false' parameter is passed here because the
@@ -1275,7 +1275,7 @@
// Thread is in thread_blocked state and oop access is unsafe.
jt->set_suspend_equivalent();
- if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) {
+ if (interruptible && (jt->is_interrupted(false) || HAS_PENDING_EXCEPTION)) {
// Intentionally empty
} else if (node._notified == 0) {
if (millis <= 0) {
@@ -1401,7 +1401,7 @@
if (!WasNotified) {
// no, it could be timeout or Thread.interrupt() or both
// check for interrupt event, otherwise it is timeout
- if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
+ if (interruptible && jt->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
THROW(vmSymbols::java_lang_InterruptedException());
}
}
--- a/src/hotspot/share/runtime/os.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/os.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -482,9 +482,6 @@
static OSReturn set_priority(Thread* thread, ThreadPriority priority);
static OSReturn get_priority(const Thread* const thread, ThreadPriority& priority);
- static void interrupt(Thread* thread);
- static bool is_interrupted(Thread* thread, bool clear_interrupted);
-
static int pd_self_suspend_thread(Thread* thread);
static ExtendedPC fetch_frame_from_context(const void* ucVoid, intptr_t** sp, intptr_t** fp);
--- a/src/hotspot/share/runtime/osThread.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/osThread.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -30,7 +30,7 @@
pd_initialize();
set_start_proc(start_proc);
set_start_parm(start_parm);
- set_interrupted(false);
+ _interrupted = 0;
}
OSThread::~OSThread() {
--- a/src/hotspot/share/runtime/osThread.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/osThread.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -82,10 +82,11 @@
void set_start_proc(OSThreadStartFunc start_proc) { _start_proc = start_proc; }
void* start_parm() const { return _start_parm; }
void set_start_parm(void* start_parm) { _start_parm = start_parm; }
-
+ // These are specialized on Windows.
+#ifndef _WINDOWS
volatile bool interrupted() const { return _interrupted != 0; }
void set_interrupted(bool z) { _interrupted = z ? 1 : 0; }
-
+#endif
// Printing
void print_on(outputStream* st) const;
void print() const;
--- a/src/hotspot/share/runtime/synchronizer.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/synchronizer.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -171,7 +171,7 @@
if (mark.has_monitor()) {
ObjectMonitor* const mon = mark.monitor();
- assert(oopDesc::equals((oop) mon->object(), obj), "invariant");
+ assert(mon->object() == obj, "invariant");
if (mon->owner() != self) return false; // slow-path for IMS exception
if (mon->first_waiter() != NULL) {
@@ -215,7 +215,7 @@
if (mark.has_monitor()) {
ObjectMonitor* const m = mark.monitor();
- assert(oopDesc::equals((oop) m->object(), obj), "invariant");
+ assert(m->object() == obj, "invariant");
Thread* const owner = (Thread *) m->_owner;
// Lock contention and Transactional Lock Elision (TLE) diagnostics
@@ -1301,7 +1301,7 @@
ObjectMonitor* inf = mark.monitor();
markWord dmw = inf->header();
assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value());
- assert(oopDesc::equals((oop) inf->object(), object), "invariant");
+ assert(inf->object() == object, "invariant");
assert(ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
return inf;
}
--- a/src/hotspot/share/runtime/thread.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/thread.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -166,7 +166,7 @@
#ifndef USE_LIBRARY_BASED_TLS_ONLY
// Current thread is maintained as a thread-local variable
-THREAD_LOCAL_DECL Thread* Thread::_thr_current = NULL;
+THREAD_LOCAL Thread* Thread::_thr_current = NULL;
#endif
// ======= Thread ========
@@ -856,19 +856,6 @@
return true;
}
-void Thread::interrupt(Thread* thread) {
- debug_only(check_for_dangling_thread_pointer(thread);)
- os::interrupt(thread);
-}
-
-bool Thread::is_interrupted(Thread* thread, bool clear_interrupted) {
- debug_only(check_for_dangling_thread_pointer(thread);)
- // Note: If clear_interrupted==false, this simply fetches and
- // returns the value of the field osthread()->interrupted().
- return os::is_interrupted(thread, clear_interrupted);
-}
-
-
// GC Support
bool Thread::claim_par_threads_do(uintx claim_token) {
uintx token = _threads_do_token;
@@ -1726,6 +1713,56 @@
assert(deferred_card_mark().is_empty(), "Default MemRegion ctor");
}
+
+// interrupt support
+
+void JavaThread::interrupt() {
+ debug_only(check_for_dangling_thread_pointer(this);)
+
+ if (!osthread()->interrupted()) {
+ osthread()->set_interrupted(true);
+ // More than one thread can get here with the same value of osthread,
+ // resulting in multiple notifications. We do, however, want the store
+ // to interrupted() to be visible to other threads before we execute unpark().
+ OrderAccess::fence();
+
+ // For JavaThread::sleep. Historically we only unpark if changing to the interrupted
+ // state, in contrast to the other events below. Not clear exactly why.
+ _SleepEvent->unpark();
+ }
+
+ // For JSR166. Unpark even if interrupt status already was set.
+ parker()->unpark();
+
+ // For ObjectMonitor and JvmtiRawMonitor
+ _ParkEvent->unpark();
+}
+
+
+bool JavaThread::is_interrupted(bool clear_interrupted) {
+ debug_only(check_for_dangling_thread_pointer(this);)
+ bool interrupted = osthread()->interrupted();
+
+ // NOTE that since there is no "lock" around the interrupt and
+ // is_interrupted operations, there is the possibility that the
+ // interrupted flag (in osThread) will be "false" but that the
+ // low-level events will be in the signaled state. This is
+ // intentional. The effect of this is that Object.wait() and
+ // LockSupport.park() will appear to have a spurious wakeup, which
+ // is allowed and not harmful, and the possibility is so rare that
+ // it is not worth the added complexity to add yet another lock.
+ // For the sleep event an explicit reset is performed on entry
+ // to JavaThread::sleep, so there is no early return. It has also been
+ // recommended not to put the interrupted flag into the "event"
+ // structure because it hides the issue.
+ if (interrupted && clear_interrupted) {
+ osthread()->set_interrupted(false);
+ // consider thread->_SleepEvent->reset() ... optional optimization
+ }
+
+ return interrupted;
+}
+
bool JavaThread::reguard_stack(address cur_sp) {
if (_stack_guard_state != stack_guard_yellow_reserved_disabled
&& _stack_guard_state != stack_guard_reserved_disabled) {
@@ -2370,8 +2407,8 @@
}
- // Interrupt thread so it will wake up from a potential wait()
- Thread::interrupt(this);
+ // Interrupt thread so it will wake up from a potential wait()/sleep()/park()
+ this->interrupt();
}
// External suspension mechanism.
@@ -3361,7 +3398,7 @@
for (;;) {
// interruption has precedence over timing out
- if (os::is_interrupted(this, true)) {
+ if (this->is_interrupted(true)) {
return false;
}
@@ -3389,7 +3426,7 @@
// time moving backwards, should only happen if no monotonic clock
// not a guarantee() because JVM should not abort on kernel/glibc bugs
assert(!os::supports_monotonic_clock(),
- "unexpected time moving backwards detected in os::sleep()");
+ "unexpected time moving backwards detected in JavaThread::sleep()");
} else {
millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
}
--- a/src/hotspot/share/runtime/thread.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/thread.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -142,7 +142,7 @@
#ifndef USE_LIBRARY_BASED_TLS_ONLY
// Current thread is maintained as a thread-local variable
- static THREAD_LOCAL_DECL Thread* _thr_current;
+ static THREAD_LOCAL Thread* _thr_current;
#endif
// Thread local data area available to the GC. The internal
@@ -514,8 +514,6 @@
static void set_priority(Thread* thread, ThreadPriority priority);
static ThreadPriority get_priority(const Thread* const thread);
static void start(Thread* thread);
- static void interrupt(Thread* thr);
- static bool is_interrupted(Thread* thr, bool clear_interrupted);
void set_native_thread_name(const char *name) {
assert(Thread::current() == this, "set_native_thread_name can only be called on the current thread");
@@ -2055,9 +2053,14 @@
InstanceKlass* _class_to_be_initialized;
// java.lang.Thread.sleep support
+ ParkEvent * _SleepEvent;
public:
- ParkEvent * _SleepEvent;
bool sleep(jlong millis);
+
+ // java.lang.Thread interruption support
+ void interrupt();
+ bool is_interrupted(bool clear_interrupted);
+
};
// Inline implementation of JavaThread::current
--- a/src/hotspot/share/runtime/vframe.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/runtime/vframe.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -135,7 +135,7 @@
//
// Skip the monitor that the thread is blocked to enter or waiting on
//
- if (!found_first_monitor && (oopDesc::equals(obj, pending_obj) || oopDesc::equals(obj, waiting_obj))) {
+ if (!found_first_monitor && (obj == pending_obj || obj == waiting_obj)) {
continue;
}
found_first_monitor = true;
--- a/src/hotspot/share/services/memoryManager.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/services/memoryManager.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -70,7 +70,7 @@
int add_pool(MemoryPool* pool);
- bool is_manager(instanceHandle mh) { return oopDesc::equals(mh(), _memory_mgr_obj); }
+ bool is_manager(instanceHandle mh) { return mh() == _memory_mgr_obj; }
virtual instanceOop get_memory_manager_instance(TRAPS);
virtual bool is_gc_memory_manager() { return false; }
--- a/src/hotspot/share/services/memoryPool.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/services/memoryPool.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -95,7 +95,7 @@
// max size could be changed
virtual size_t max_size() const { return _max_size; }
- bool is_pool(instanceHandle pool) { return oopDesc::equals(pool(), _memory_pool_obj); }
+ bool is_pool(instanceHandle pool) { return pool() == _memory_pool_obj; }
bool available_for_allocation() { return _available_for_allocation; }
bool set_available_for_allocation(bool value) {
--- a/src/hotspot/share/services/threadService.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/services/threadService.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -680,7 +680,7 @@
for (int j = 0; j < len; j++) {
oop monitor = locked_monitors->at(j);
assert(monitor != NULL, "must be a Java object");
- if (oopDesc::equals(monitor, object)) {
+ if (monitor == object) {
found = true;
break;
}
--- a/src/hotspot/share/utilities/exceptions.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/exceptions.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -435,9 +435,9 @@
volatile int Exceptions::_out_of_memory_error_class_metaspace_errors = 0;
void Exceptions::count_out_of_memory_exceptions(Handle exception) {
- if (oopDesc::equals(exception(), Universe::out_of_memory_error_metaspace())) {
+ if (exception() == Universe::out_of_memory_error_metaspace()) {
Atomic::inc(&_out_of_memory_error_metaspace_errors);
- } else if (oopDesc::equals(exception(), Universe::out_of_memory_error_class_metaspace())) {
+ } else if (exception() == Universe::out_of_memory_error_class_metaspace()) {
Atomic::inc(&_out_of_memory_error_class_metaspace_errors);
} else {
// everything else reported as java heap OOM
--- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -254,9 +254,7 @@
#define JLONG_FORMAT_W(width) "%" #width "ld"
#endif // _LP64 && __APPLE__
-#ifndef USE_LIBRARY_BASED_TLS_ONLY
-#define THREAD_LOCAL_DECL __thread
-#endif
+#define THREAD_LOCAL __thread
// Inlining support
#define NOINLINE __attribute__ ((noinline))
--- a/src/hotspot/share/utilities/globalDefinitions_solstudio.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/globalDefinitions_solstudio.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -245,9 +245,7 @@
#define offset_of(klass,field) offsetof(klass,field)
-#ifndef USE_LIBRARY_BASED_TLS_ONLY
-#define THREAD_LOCAL_DECL __thread
-#endif
+#define THREAD_LOCAL __thread
// Inlining support
#define NOINLINE
--- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -153,9 +153,7 @@
#define offset_of(klass,field) offsetof(klass,field)
-#ifndef USE_LIBRARY_BASED_TLS_ONLY
-#define THREAD_LOCAL_DECL __declspec( thread )
-#endif
+#define THREAD_LOCAL __declspec(thread)
// Inlining support
// MSVC has '__declspec(noinline)' but according to the official documentation
--- a/src/hotspot/share/utilities/globalDefinitions_xlc.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/globalDefinitions_xlc.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -130,10 +130,6 @@
// AIX 5.3 has buggy __thread support. (see JDK-8176442).
#define USE_LIBRARY_BASED_TLS_ONLY 1
-#ifndef USE_LIBRARY_BASED_TLS_ONLY
-#define THREAD_LOCAL_DECL __thread
-#endif
-
// Inlining support
//
// Be aware that for function/method declarations, xlC only supports the following
--- a/src/hotspot/share/utilities/growableArray.hpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/growableArray.hpp Wed Sep 18 07:46:02 2019 +0200
@@ -218,15 +218,6 @@
void print();
- inline static bool safe_equals(oop obj1, oop obj2) {
- return oopDesc::equals(obj1, obj2);
- }
-
- template <class X>
- inline static bool safe_equals(X i1, X i2) {
- return i1 == i2;
- }
-
int append(const E& elem) {
check_nesting();
if (_len == _max) grow(_len);
@@ -311,7 +302,7 @@
bool contains(const E& elem) const {
for (int i = 0; i < _len; i++) {
- if (safe_equals(_data[i], elem)) return true;
+ if (_data[i] == elem) return true;
}
return false;
}
--- a/src/hotspot/share/utilities/ostream.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/hotspot/share/utilities/ostream.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -99,13 +99,14 @@
result_len = strlen(result);
if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate
} else {
- int written = os::vsnprintf(buffer, buflen, format, ap);
- assert(written >= 0, "vsnprintf encoding error");
+ int required_len = os::vsnprintf(buffer, buflen, format, ap);
+ assert(required_len >= 0, "vsnprintf encoding error");
result = buffer;
- if ((size_t)written < buflen) {
- result_len = written;
+ if ((size_t)required_len < buflen) {
+ result_len = required_len;
} else {
- DEBUG_ONLY(warning("increase O_BUFLEN in ostream.hpp -- output truncated");)
+ DEBUG_ONLY(warning("outputStream::do_vsnprintf output truncated -- buffer length is %d bytes but %d bytes are needed.",
+ add_cr ? (int)buflen + 1 : (int)buflen, add_cr ? required_len + 2 : required_len + 1);)
result_len = buflen - 1;
}
}
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -62,22 +62,14 @@
}
// step 2: find mount point
- UnixPath parent = path.getParent();
- while (parent != null) {
- UnixFileAttributes attrs = null;
- try {
- attrs = UnixFileAttributes.get(parent, true);
- } catch (UnixException x) {
- x.rethrowAsIOException(parent);
- }
- if (attrs.dev() != dev())
- break;
- path = parent;
- parent = parent.getParent();
+ byte[] dir = null;
+ try {
+ dir = BsdNativeDispatcher.getmntonname(path);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(path);
}
// step 3: lookup mounted file systems
- byte[] dir = path.asByteArray();
for (UnixMountEntry entry: fs.getMountEntries()) {
if (Arrays.equals(dir, entry.dir()))
return entry;
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -51,6 +51,20 @@
*/
static native void endfsstat(long iter) throws UnixException;
+ /**
+ * int statfs(const char *path, struct statfs *buf);
+ * returns buf->f_mntonname (directory on which mounted)
+ */
+ static byte[] getmntonname(UnixPath path) throws UnixException {
+ NativeBuffer pathBuffer = copyToNativeBuffer(path);
+ try {
+ return getmntonname0(pathBuffer.address());
+ } finally {
+ pathBuffer.release();
+ }
+ }
+ static native byte[] getmntonname0(long pathAddress) throws UnixException;
+
// initialize field IDs
private static native void initIDs();
--- a/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -203,3 +203,24 @@
free(iter);
}
}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_BsdNativeDispatcher_getmntonname0(JNIEnv *env, jclass this,
+ jlong pathAddress)
+{
+ struct statfs buf;
+ const char* path = (const char*)jlong_to_ptr(pathAddress);
+
+ if (statfs(path, &buf) != 0) {
+ throwUnixException(env, errno);
+ }
+
+ jsize len = strlen(buf.f_mntonname);
+ jbyteArray mntonname = (*env)->NewByteArray(env, len);
+ if (mntonname != NULL) {
+ (*env)->SetByteArrayRegion(env, mntonname, 0, len,
+ (jbyte*)buf.f_mntonname);
+ }
+
+ return mntonname;
+}
--- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Wed Sep 18 07:46:02 2019 +0200
@@ -385,7 +385,7 @@
* cases where old nodes can be reused because their next fields
* won't change. On average, only about one-sixth of them need
* cloning when a table doubles. The nodes they replace will be
- * garbage collectable as soon as they are no longer referenced by
+ * garbage collectible as soon as they are no longer referenced by
* any reader thread that may be in the midst of concurrently
* traversing table. Upon transfer, the old table bin contains
* only a special forwarding node (with hash field "MOVED") that
@@ -3286,9 +3286,8 @@
return true;
}
- private static final Unsafe U = Unsafe.getUnsafe();
private static final long LOCKSTATE
- = U.objectFieldOffset(TreeBin.class, "lockState");
+ = U.objectFieldOffset(TreeBin.class, "lockState");
}
/* ----------------Table Traversal -------------- */
@@ -6345,28 +6344,20 @@
// Unsafe mechanics
private static final Unsafe U = Unsafe.getUnsafe();
- private static final long SIZECTL;
- private static final long TRANSFERINDEX;
- private static final long BASECOUNT;
- private static final long CELLSBUSY;
- private static final long CELLVALUE;
- private static final int ABASE;
+ private static final long SIZECTL
+ = U.objectFieldOffset(ConcurrentHashMap.class, "sizeCtl");
+ private static final long TRANSFERINDEX
+ = U.objectFieldOffset(ConcurrentHashMap.class, "transferIndex");
+ private static final long BASECOUNT
+ = U.objectFieldOffset(ConcurrentHashMap.class, "baseCount");
+ private static final long CELLSBUSY
+ = U.objectFieldOffset(ConcurrentHashMap.class, "cellsBusy");
+ private static final long CELLVALUE
+ = U.objectFieldOffset(CounterCell.class, "value");
+ private static final int ABASE = U.arrayBaseOffset(Node[].class);
private static final int ASHIFT;
static {
- SIZECTL = U.objectFieldOffset
- (ConcurrentHashMap.class, "sizeCtl");
- TRANSFERINDEX = U.objectFieldOffset
- (ConcurrentHashMap.class, "transferIndex");
- BASECOUNT = U.objectFieldOffset
- (ConcurrentHashMap.class, "baseCount");
- CELLSBUSY = U.objectFieldOffset
- (ConcurrentHashMap.class, "cellsBusy");
-
- CELLVALUE = U.objectFieldOffset
- (CounterCell.class, "value");
-
- ABASE = U.arrayBaseOffset(Node[].class);
int scale = U.arrayIndexScale(Node[].class);
if ((scale & (scale - 1)) != 0)
throw new ExceptionInInitializerError("array index scale not a power of two");
--- a/src/java.base/share/classes/java/util/concurrent/Phaser.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/Phaser.java Wed Sep 18 07:46:02 2019 +0200
@@ -97,7 +97,7 @@
* associated recovery within handlers of those exceptions,
* often after invoking {@code forceTermination}. Phasers may
* also be used by tasks executing in a {@link ForkJoinPool}.
- * Progress is ensured if the pool's parallelismLevel can
+ * Progress is ensured if the pool's parallelism level can
* accommodate the maximum number of simultaneously blocked
* parties.
*
--- a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Wed Sep 18 07:46:02 2019 +0200
@@ -1053,18 +1053,18 @@
// Unsafe mechanics
private static final Unsafe U = Unsafe.getUnsafe();
- private static final long SEED = U.objectFieldOffset
- (Thread.class, "threadLocalRandomSeed");
- private static final long PROBE = U.objectFieldOffset
- (Thread.class, "threadLocalRandomProbe");
- private static final long SECONDARY = U.objectFieldOffset
- (Thread.class, "threadLocalRandomSecondarySeed");
- private static final long THREADLOCALS = U.objectFieldOffset
- (Thread.class, "threadLocals");
- private static final long INHERITABLETHREADLOCALS = U.objectFieldOffset
- (Thread.class, "inheritableThreadLocals");
- private static final long INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
- (Thread.class, "inheritedAccessControlContext");
+ private static final long SEED
+ = U.objectFieldOffset(Thread.class, "threadLocalRandomSeed");
+ private static final long PROBE
+ = U.objectFieldOffset(Thread.class, "threadLocalRandomProbe");
+ private static final long SECONDARY
+ = U.objectFieldOffset(Thread.class, "threadLocalRandomSecondarySeed");
+ private static final long THREADLOCALS
+ = U.objectFieldOffset(Thread.class, "threadLocals");
+ private static final long INHERITABLETHREADLOCALS
+ = U.objectFieldOffset(Thread.class, "inheritableThreadLocals");
+ private static final long INHERITEDACCESSCONTROLCONTEXT
+ = U.objectFieldOffset(Thread.class, "inheritedAccessControlContext");
/** Rarely-used holder for the second of a pair of Gaussians */
private static final ThreadLocal<Double> nextLocalGaussian =
--- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Wed Sep 18 07:46:02 2019 +0200
@@ -38,6 +38,7 @@
import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
+import jdk.internal.misc.Unsafe;
/**
* An {@code int} value that may be updated atomically. See the
@@ -58,8 +59,9 @@
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final long VALUE
+ = U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
--- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Wed Sep 18 07:46:02 2019 +0200
@@ -38,6 +38,7 @@
import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
+import jdk.internal.misc.Unsafe;
/**
* A {@code long} value that may be updated atomically. See the
@@ -72,8 +73,9 @@
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long VALUE = U.objectFieldOffset(AtomicLong.class, "value");
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final long VALUE
+ = U.objectFieldOffset(AtomicLong.class, "value");
private volatile long value;
--- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Wed Sep 18 07:46:02 2019 +0200
@@ -35,13 +35,12 @@
package java.util.concurrent.locks;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.AbstractQueuedSynchronizer.Node;
+import java.util.concurrent.ForkJoinPool;
+import jdk.internal.misc.Unsafe;
/**
* A version of {@link AbstractQueuedSynchronizer} in
@@ -73,23 +72,76 @@
* keep it that way.
*/
- /**
- * Creates a new {@code AbstractQueuedLongSynchronizer} instance
- * with initial synchronization state of zero.
- */
- protected AbstractQueuedLongSynchronizer() { }
+ // Node status bits, also used as argument and return values
+ static final int WAITING = 1; // must be 1
+ static final int CANCELLED = 0x80000000; // must be negative
+ static final int COND = 2; // in a condition wait
+
+ /** CLH Nodes */
+ abstract static class Node {
+ volatile Node prev; // initially attached via casTail
+ volatile Node next; // visibly nonnull when signallable
+ Thread waiter; // visibly nonnull when enqueued
+ volatile int status; // written by owner, atomic bit ops by others
+
+ // methods for atomic operations
+ final boolean casPrev(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, PREV, c, v);
+ }
+ final boolean casNext(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, NEXT, c, v);
+ }
+ final int getAndUnsetStatus(int v) { // for signalling
+ return U.getAndBitwiseAndInt(this, STATUS, ~v);
+ }
+ final void setPrevRelaxed(Node p) { // for off-queue assignment
+ U.putReference(this, PREV, p);
+ }
+ final void setStatusRelaxed(int s) { // for off-queue assignment
+ U.putInt(this, STATUS, s);
+ }
+ final void clearStatus() { // for reducing unneeded signals
+ U.putIntOpaque(this, STATUS, 0);
+ }
+
+ private static final long STATUS
+ = U.objectFieldOffset(Node.class, "status");
+ private static final long NEXT
+ = U.objectFieldOffset(Node.class, "next");
+ private static final long PREV
+ = U.objectFieldOffset(Node.class, "prev");
+ }
+
+ // Concrete classes tagged by type
+ static final class ExclusiveNode extends Node { }
+ static final class SharedNode extends Node { }
+
+ static final class ConditionNode extends Node
+ implements ForkJoinPool.ManagedBlocker {
+ ConditionNode nextWaiter; // link to next waiting node
+
+ /**
+ * Allows Conditions to be used in ForkJoinPools without
+ * risking fixed pool exhaustion. This is usable only for
+ * untimed Condition waits, not timed versions.
+ */
+ public final boolean isReleasable() {
+ return status <= 1 || Thread.currentThread().isInterrupted();
+ }
+
+ public final boolean block() {
+ while (!isReleasable()) LockSupport.park(this);
+ return true;
+ }
+ }
/**
- * Head of the wait queue, lazily initialized. Except for
- * initialization, it is modified only via method setHead. Note:
- * If head exists, its waitStatus is guaranteed not to be
- * CANCELLED.
+ * Head of the wait queue, lazily initialized.
*/
private transient volatile Node head;
/**
- * Tail of the wait queue, lazily initialized. Modified only via
- * method enq to add new wait node.
+ * Tail of the wait queue. After initialization, modified only via casTail.
*/
private transient volatile Node tail;
@@ -113,8 +165,7 @@
* @param newState the new state value
*/
protected final void setState(long newState) {
- // See JDK-8180620: Clarify VarHandle mixed-access subtleties
- STATE.setVolatile(this, newState);
+ state = newState;
}
/**
@@ -129,481 +180,234 @@
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(long expect, long update) {
- return STATE.compareAndSet(this, expect, update);
+ return U.compareAndSetLong(this, STATE, expect, update);
}
// Queuing utilities
- /**
- * The number of nanoseconds for which it is faster to spin
- * rather than to use timed park. A rough estimate suffices
- * to improve responsiveness with very short timeouts.
- */
- static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
+ private boolean casTail(Node c, Node v) {
+ return U.compareAndSetReference(this, TAIL, c, v);
+ }
+
+ /** tries once to CAS a new dummy node for head */
+ private void tryInitializeHead() {
+ Node h = new ExclusiveNode();
+ if (U.compareAndSetReference(this, HEAD, null, h))
+ tail = h;
+ }
/**
- * Inserts node into queue, initializing if necessary. See picture above.
- * @param node the node to insert
- * @return node's predecessor
+ * Enqueues the node unless null. (Currently used only for
+ * ConditionNodes; other cases are interleaved with acquires.)
*/
- private Node enq(Node node) {
- for (;;) {
- Node oldTail = tail;
- if (oldTail != null) {
- node.setPrevRelaxed(oldTail);
- if (compareAndSetTail(oldTail, node)) {
- oldTail.next = node;
- return oldTail;
+ final void enqueue(Node node) {
+ if (node != null) {
+ for (;;) {
+ Node t = tail;
+ node.setPrevRelaxed(t); // avoid unnecessary fence
+ if (t == null) // initialize
+ tryInitializeHead();
+ else if (casTail(t, node)) {
+ t.next = node;
+ if (t.status < 0) // wake up to clean link
+ LockSupport.unpark(node.waiter);
+ break;
}
- } else {
- initializeSyncQueue();
}
}
}
+ /** Returns true if node is found in traversal from tail */
+ final boolean isEnqueued(Node node) {
+ for (Node t = tail; t != null; t = t.prev)
+ if (t == node)
+ return true;
+ return false;
+ }
+
/**
- * Creates and enqueues node for current thread and given mode.
+ * Wakes up the successor of given node, if one exists, and unsets its
+ * WAITING status to avoid park race. This may fail to wake up an
+ * eligible thread when one or more have been cancelled, but
+ * cancelAcquire ensures liveness.
+ */
+ private static void signalNext(Node h) {
+ Node s;
+ if (h != null && (s = h.next) != null && s.status != 0) {
+ s.getAndUnsetStatus(WAITING);
+ LockSupport.unpark(s.waiter);
+ }
+ }
+
+ /** Wakes up the given node if in shared mode */
+ private static void signalNextIfShared(Node h) {
+ Node s;
+ if (h != null && (s = h.next) != null &&
+ (s instanceof SharedNode) && s.status != 0) {
+ s.getAndUnsetStatus(WAITING);
+ LockSupport.unpark(s.waiter);
+ }
+ }
+
+ /**
+ * Main acquire method, invoked by all exported acquire methods.
*
- * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
- * @return the new node
+ * @param node null unless a reacquiring Condition
+ * @param arg the acquire argument
+ * @param shared true if shared mode else exclusive
+ * @param interruptible if abort and return negative on interrupt
+ * @param timed if true use timed waits
+ * @param time if timed, the System.nanoTime value to timeout
+ * @return positive if acquired, 0 if timed out, negative if interrupted
*/
- private Node addWaiter(Node mode) {
- Node node = new Node(mode);
+ final int acquire(Node node, long arg, boolean shared,
+ boolean interruptible, boolean timed, long time) {
+ Thread current = Thread.currentThread();
+ byte spins = 0, postSpins = 0; // retries upon unpark of first thread
+ boolean interrupted = false, first = false;
+ Node pred = null; // predecessor of node when enqueued
+
+ /*
+ * Repeatedly:
+ * Check if node now first
+ * if so, ensure head stable, else ensure valid predecessor
+ * if node is first or not yet enqueued, try acquiring
+ * else if node not yet created, create it
+ * else if not yet enqueued, try once to enqueue
+ * else if woken from park, retry (up to postSpins times)
+ * else if WAITING status not set, set and retry
+ * else park and clear WAITING status, and check cancellation
+ */
for (;;) {
- Node oldTail = tail;
- if (oldTail != null) {
- node.setPrevRelaxed(oldTail);
- if (compareAndSetTail(oldTail, node)) {
- oldTail.next = node;
- return node;
+ if (!first && (pred = (node == null) ? null : node.prev) != null &&
+ !(first = (head == pred))) {
+ if (pred.status < 0) {
+ cleanQueue(); // predecessor cancelled
+ continue;
+ } else if (pred.prev == null) {
+ Thread.onSpinWait(); // ensure serialization
+ continue;
+ }
+ }
+ if (first || pred == null) {
+ boolean acquired;
+ try {
+ if (shared)
+ acquired = (tryAcquireShared(arg) >= 0);
+ else
+ acquired = tryAcquire(arg);
+ } catch (Throwable ex) {
+ cancelAcquire(node, interrupted, false);
+ throw ex;
+ }
+ if (acquired) {
+ if (first) {
+ node.prev = null;
+ head = node;
+ pred.next = null;
+ node.waiter = null;
+ if (shared)
+ signalNextIfShared(node);
+ if (interrupted)
+ current.interrupt();
+ }
+ return 1;
}
+ }
+ if (node == null) { // allocate; retry before enqueue
+ if (shared)
+ node = new SharedNode();
+ else
+ node = new ExclusiveNode();
+ } else if (pred == null) { // try to enqueue
+ node.waiter = current;
+ Node t = tail;
+ node.setPrevRelaxed(t); // avoid unnecessary fence
+ if (t == null)
+ tryInitializeHead();
+ else if (!casTail(t, node))
+ node.setPrevRelaxed(null); // back out
+ else
+ t.next = node;
+ } else if (first && spins != 0) {
+ --spins; // reduce unfairness on rewaits
+ Thread.onSpinWait();
+ } else if (node.status == 0) {
+ node.status = WAITING; // enable signal and recheck
} else {
- initializeSyncQueue();
+ long nanos;
+ spins = postSpins = (byte)((postSpins << 1) | 1);
+ if (!timed)
+ LockSupport.park(this);
+ else if ((nanos = time - System.nanoTime()) > 0L)
+ LockSupport.parkNanos(this, nanos);
+ else
+ break;
+ node.clearStatus();
+ if ((interrupted |= Thread.interrupted()) && interruptible)
+ break;
+ }
+ }
+ return cancelAcquire(node, interrupted, interruptible);
+ }
+
+ /**
+ * Possibly repeatedly traverses from tail, unsplicing cancelled
+ * nodes until none are found.
+ */
+ private void cleanQueue() {
+ for (;;) { // restart point
+ for (Node q = tail, s = null, p, n;;) { // (p, q, s) triples
+ if (q == null || (p = q.prev) == null)
+ return; // end of list
+ if (s == null ? tail != q : (s.prev != q || s.status < 0))
+ break; // inconsistent
+ if (q.status < 0) { // cancelled
+ if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
+ q.prev == p) {
+ p.casNext(q, s); // OK if fails
+ if (p.prev == null)
+ signalNext(p);
+ }
+ break;
+ }
+ if ((n = p.next) != q) { // help finish
+ if (n != null && q.prev == p) {
+ p.casNext(n, q);
+ if (p.prev == null)
+ signalNext(p);
+ }
+ break;
+ }
+ s = q;
+ q = q.prev;
}
}
}
/**
- * Sets head of queue to be node, thus dequeuing. Called only by
- * acquire methods. Also nulls out unused fields for sake of GC
- * and to suppress unnecessary signals and traversals.
- *
- * @param node the node
- */
- private void setHead(Node node) {
- head = node;
- node.thread = null;
- node.prev = null;
- }
-
- /**
- * Wakes up node's successor, if one exists.
- *
- * @param node the node
- */
- private void unparkSuccessor(Node node) {
- /*
- * If status is negative (i.e., possibly needing signal) try
- * to clear in anticipation of signalling. It is OK if this
- * fails or if status is changed by waiting thread.
- */
- int ws = node.waitStatus;
- if (ws < 0)
- node.compareAndSetWaitStatus(ws, 0);
-
- /*
- * Thread to unpark is held in successor, which is normally
- * just the next node. But if cancelled or apparently null,
- * traverse backwards from tail to find the actual
- * non-cancelled successor.
- */
- Node s = node.next;
- if (s == null || s.waitStatus > 0) {
- s = null;
- for (Node p = tail; p != node && p != null; p = p.prev)
- if (p.waitStatus <= 0)
- s = p;
- }
- if (s != null)
- LockSupport.unpark(s.thread);
- }
-
- /**
- * Release action for shared mode -- signals successor and ensures
- * propagation. (Note: For exclusive mode, release just amounts
- * to calling unparkSuccessor of head if it needs signal.)
- */
- private void doReleaseShared() {
- /*
- * Ensure that a release propagates, even if there are other
- * in-progress acquires/releases. This proceeds in the usual
- * way of trying to unparkSuccessor of head if it needs
- * signal. But if it does not, status is set to PROPAGATE to
- * ensure that upon release, propagation continues.
- * Additionally, we must loop in case a new node is added
- * while we are doing this. Also, unlike other uses of
- * unparkSuccessor, we need to know if CAS to reset status
- * fails, if so rechecking.
- */
- for (;;) {
- Node h = head;
- if (h != null && h != tail) {
- int ws = h.waitStatus;
- if (ws == Node.SIGNAL) {
- if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
- continue; // loop to recheck cases
- unparkSuccessor(h);
- }
- else if (ws == 0 &&
- !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
- continue; // loop on failed CAS
- }
- if (h == head) // loop if head changed
- break;
- }
- }
-
- /**
- * Sets head of queue, and checks if successor may be waiting
- * in shared mode, if so propagating if either propagate > 0 or
- * PROPAGATE status was set.
- *
- * @param node the node
- * @param propagate the return value from a tryAcquireShared
- */
- private void setHeadAndPropagate(Node node, long propagate) {
- Node h = head; // Record old head for check below
- setHead(node);
- /*
- * Try to signal next queued node if:
- * Propagation was indicated by caller,
- * or was recorded (as h.waitStatus either before
- * or after setHead) by a previous operation
- * (note: this uses sign-check of waitStatus because
- * PROPAGATE status may transition to SIGNAL.)
- * and
- * The next node is waiting in shared mode,
- * or we don't know, because it appears null
- *
- * The conservatism in both of these checks may cause
- * unnecessary wake-ups, but only when there are multiple
- * racing acquires/releases, so most need signals now or soon
- * anyway.
- */
- if (propagate > 0 || h == null || h.waitStatus < 0 ||
- (h = head) == null || h.waitStatus < 0) {
- Node s = node.next;
- if (s == null || s.isShared())
- doReleaseShared();
- }
- }
-
- // Utilities for various versions of acquire
-
- /**
* Cancels an ongoing attempt to acquire.
*
- * @param node the node
- */
- private void cancelAcquire(Node node) {
- // Ignore if node doesn't exist
- if (node == null)
- return;
-
- node.thread = null;
-
- // Skip cancelled predecessors
- Node pred = node.prev;
- while (pred.waitStatus > 0)
- node.prev = pred = pred.prev;
-
- // predNext is the apparent node to unsplice. CASes below will
- // fail if not, in which case, we lost race vs another cancel
- // or signal, so no further action is necessary, although with
- // a possibility that a cancelled node may transiently remain
- // reachable.
- Node predNext = pred.next;
-
- // Can use unconditional write instead of CAS here.
- // After this atomic step, other Nodes can skip past us.
- // Before, we are free of interference from other threads.
- node.waitStatus = Node.CANCELLED;
-
- // If we are the tail, remove ourselves.
- if (node == tail && compareAndSetTail(node, pred)) {
- pred.compareAndSetNext(predNext, null);
- } else {
- // If successor needs signal, try to set pred's next-link
- // so it will get one. Otherwise wake it up to propagate.
- int ws;
- if (pred != head &&
- ((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
- pred.thread != null) {
- Node next = node.next;
- if (next != null && next.waitStatus <= 0)
- pred.compareAndSetNext(predNext, next);
- } else {
- unparkSuccessor(node);
- }
-
- node.next = node; // help GC
- }
- }
-
- /**
- * Checks and updates status for a node that failed to acquire.
- * Returns true if thread should block. This is the main signal
- * control in all acquire loops. Requires that pred == node.prev.
- *
- * @param pred node's predecessor holding status
- * @param node the node
- * @return {@code true} if thread should block
- */
- private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int ws = pred.waitStatus;
- if (ws == Node.SIGNAL)
- /*
- * This node has already set status asking a release
- * to signal it, so it can safely park.
- */
- return true;
- if (ws > 0) {
- /*
- * Predecessor was cancelled. Skip over predecessors and
- * indicate retry.
- */
- do {
- node.prev = pred = pred.prev;
- } while (pred.waitStatus > 0);
- pred.next = node;
- } else {
- /*
- * waitStatus must be 0 or PROPAGATE. Indicate that we
- * need a signal, but don't park yet. Caller will need to
- * retry to make sure it cannot acquire before parking.
- */
- pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
- }
- return false;
- }
-
- /**
- * Convenience method to interrupt current thread.
- */
- static void selfInterrupt() {
- Thread.currentThread().interrupt();
- }
-
- /**
- * Convenience method to park and then check if interrupted.
- *
- * @return {@code true} if interrupted
- */
- private final boolean parkAndCheckInterrupt() {
- LockSupport.park(this);
- return Thread.interrupted();
- }
-
- /*
- * Various flavors of acquire, varying in exclusive/shared and
- * control modes. Each is mostly the same, but annoyingly
- * different. Only a little bit of factoring is possible due to
- * interactions of exception mechanics (including ensuring that we
- * cancel if tryAcquire throws exception) and other control, at
- * least not without hurting performance too much.
- */
-
- /**
- * Acquires in exclusive uninterruptible mode for thread already in
- * queue. Used by condition wait methods as well as acquire.
- *
- * @param node the node
- * @param arg the acquire argument
- * @return {@code true} if interrupted while waiting
- */
- final boolean acquireQueued(final Node node, long arg) {
- boolean interrupted = false;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return interrupted;
- }
- if (shouldParkAfterFailedAcquire(p, node))
- interrupted |= parkAndCheckInterrupt();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- if (interrupted)
- selfInterrupt();
- throw t;
- }
- }
-
- /**
- * Acquires in exclusive interruptible mode.
- * @param arg the acquire argument
+ * @param node the node (may be null if cancelled before enqueuing)
+ * @param interrupted true if thread interrupted
+ * @param interruptible if should report interruption vs reset
*/
- private void doAcquireInterruptibly(long arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.EXCLUSIVE);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
- }
-
- /**
- * Acquires in exclusive timed mode.
- *
- * @param arg the acquire argument
- * @param nanosTimeout max wait time
- * @return {@code true} if acquired
- */
- private boolean doAcquireNanos(long arg, long nanosTimeout)
- throws InterruptedException {
- if (nanosTimeout <= 0L)
- return false;
- final long deadline = System.nanoTime() + nanosTimeout;
- final Node node = addWaiter(Node.EXCLUSIVE);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return true;
- }
- nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L) {
- cancelAcquire(node);
- return false;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
+ private int cancelAcquire(Node node, boolean interrupted,
+ boolean interruptible) {
+ if (node != null) {
+ node.waiter = null;
+ node.status = CANCELLED;
+ if (node.prev != null)
+ cleanQueue();
}
- }
-
- /**
- * Acquires in shared uninterruptible mode.
- * @param arg the acquire argument
- */
- private void doAcquireShared(long arg) {
- final Node node = addWaiter(Node.SHARED);
- boolean interrupted = false;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- long r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node))
- interrupted |= parkAndCheckInterrupt();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- } finally {
- if (interrupted)
- selfInterrupt();
+ if (interrupted) {
+ if (interruptible)
+ return CANCELLED;
+ else
+ Thread.currentThread().interrupt();
}
- }
-
- /**
- * Acquires in shared interruptible mode.
- * @param arg the acquire argument
- */
- private void doAcquireSharedInterruptibly(long arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.SHARED);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- long r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
- }
-
- /**
- * Acquires in shared timed mode.
- *
- * @param arg the acquire argument
- * @param nanosTimeout max wait time
- * @return {@code true} if acquired
- */
- private boolean doAcquireSharedNanos(long arg, long nanosTimeout)
- throws InterruptedException {
- if (nanosTimeout <= 0L)
- return false;
- final long deadline = System.nanoTime() + nanosTimeout;
- final Node node = addWaiter(Node.SHARED);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- long r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return true;
- }
- }
- nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L) {
- cancelAcquire(node);
- return false;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
+ return 0;
}
// Main exported methods
@@ -756,9 +560,8 @@
* can represent anything you like.
*/
public final void acquire(long arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
+ if (!tryAcquire(arg))
+ acquire(null, arg, false, false, false, 0L);
}
/**
@@ -776,11 +579,10 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(long arg)
- throws InterruptedException {
- if (Thread.interrupted())
+ throws InterruptedException {
+ if (Thread.interrupted() ||
+ (!tryAcquire(arg) && acquire(null, arg, false, true, false, 0L) < 0))
throw new InterruptedException();
- if (!tryAcquire(arg))
- doAcquireInterruptibly(arg);
}
/**
@@ -801,11 +603,20 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final boolean tryAcquireNanos(long arg, long nanosTimeout)
- throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquire(arg) ||
- doAcquireNanos(arg, nanosTimeout);
+ throws InterruptedException {
+ if (!Thread.interrupted()) {
+ if (tryAcquire(arg))
+ return true;
+ if (nanosTimeout <= 0L)
+ return false;
+ int stat = acquire(null, arg, false, true, true,
+ System.nanoTime() + nanosTimeout);
+ if (stat > 0)
+ return true;
+ if (stat == 0)
+ return false;
+ }
+ throw new InterruptedException();
}
/**
@@ -820,9 +631,7 @@
*/
public final boolean release(long arg) {
if (tryRelease(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
+ signalNext(head);
return true;
}
return false;
@@ -841,7 +650,7 @@
*/
public final void acquireShared(long arg) {
if (tryAcquireShared(arg) < 0)
- doAcquireShared(arg);
+ acquire(null, arg, true, false, false, 0L);
}
/**
@@ -858,11 +667,11 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireSharedInterruptibly(long arg)
- throws InterruptedException {
- if (Thread.interrupted())
+ throws InterruptedException {
+ if (Thread.interrupted() ||
+ (tryAcquireShared(arg) < 0 &&
+ acquire(null, arg, true, true, false, 0L) < 0))
throw new InterruptedException();
- if (tryAcquireShared(arg) < 0)
- doAcquireSharedInterruptibly(arg);
}
/**
@@ -883,10 +692,19 @@
*/
public final boolean tryAcquireSharedNanos(long arg, long nanosTimeout)
throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquireShared(arg) >= 0 ||
- doAcquireSharedNanos(arg, nanosTimeout);
+ if (!Thread.interrupted()) {
+ if (tryAcquireShared(arg) >= 0)
+ return true;
+ if (nanosTimeout <= 0L)
+ return false;
+ int stat = acquire(null, arg, true, true, true,
+ System.nanoTime() + nanosTimeout);
+ if (stat > 0)
+ return true;
+ if (stat == 0)
+ return false;
+ }
+ throw new InterruptedException();
}
/**
@@ -900,7 +718,7 @@
*/
public final boolean releaseShared(long arg) {
if (tryReleaseShared(arg)) {
- doReleaseShared();
+ signalNext(head);
return true;
}
return false;
@@ -918,7 +736,7 @@
*/
public final boolean hasQueuedThreads() {
for (Node p = tail, h = head; p != h && p != null; p = p.prev)
- if (p.waitStatus <= 0)
+ if (p.status >= 0)
return true;
return false;
}
@@ -948,45 +766,16 @@
* {@code null} if no threads are currently queued
*/
public final Thread getFirstQueuedThread() {
- // handle only fast path, else relay
- return (head == tail) ? null : fullGetFirstQueuedThread();
- }
-
- /**
- * Version of getFirstQueuedThread called when fastpath fails.
- */
- private Thread fullGetFirstQueuedThread() {
- /*
- * The first node is normally head.next. Try to get its
- * thread field, ensuring consistent reads: If thread
- * field is nulled out or s.prev is no longer head, then
- * some other thread(s) concurrently performed setHead in
- * between some of our reads. We try this twice before
- * resorting to traversal.
- */
- Node h, s;
- Thread st;
- if (((h = head) != null && (s = h.next) != null &&
- s.prev == head && (st = s.thread) != null) ||
- ((h = head) != null && (s = h.next) != null &&
- s.prev == head && (st = s.thread) != null))
- return st;
-
- /*
- * Head's next field might not have been set yet, or may have
- * been unset after setHead. So we must check to see if tail
- * is actually first node. If not, we continue on, safely
- * traversing from tail back to head to find first,
- * guaranteeing termination.
- */
-
- Thread firstThread = null;
- for (Node p = tail; p != null && p != head; p = p.prev) {
- Thread t = p.thread;
- if (t != null)
- firstThread = t;
+ Thread first = null, w; Node h, s;
+ if ((h = head) != null && ((s = h.next) == null ||
+ (first = s.waiter) == null ||
+ s.prev == null)) {
+ // traverse from tail on stale reads
+ for (Node p = tail, q; p != null && (q = p.prev) != null; p = q)
+ if ((w = p.waiter) != null)
+ first = w;
}
- return firstThread;
+ return first;
}
/**
@@ -1003,7 +792,7 @@
if (thread == null)
throw new NullPointerException();
for (Node p = tail; p != null; p = p.prev)
- if (p.thread == thread)
+ if (p.waiter == thread)
return true;
return false;
}
@@ -1019,10 +808,8 @@
*/
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
- return (h = head) != null &&
- (s = h.next) != null &&
- !s.isShared() &&
- s.thread != null;
+ return (h = head) != null && (s = h.next) != null &&
+ !(s instanceof SharedNode) && s.waiter != null;
}
/**
@@ -1052,7 +839,7 @@
* synchronizer might look like this:
*
* <pre> {@code
- * protected boolean tryAcquire(int arg) {
+ * protected boolean tryAcquire(long arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
* return true;
@@ -1069,19 +856,12 @@
* @since 1.7
*/
public final boolean hasQueuedPredecessors() {
- Node h, s;
- if ((h = head) != null) {
- if ((s = h.next) == null || s.waitStatus > 0) {
- s = null; // traverse in case of concurrent cancellation
- for (Node p = tail; p != h && p != null; p = p.prev) {
- if (p.waitStatus <= 0)
- s = p;
- }
- }
- if (s != null && s.thread != Thread.currentThread())
- return true;
- }
- return false;
+ Thread first = null; Node h, s;
+ if ((h = head) != null && ((s = h.next) == null ||
+ (first = s.waiter) == null ||
+ s.prev == null))
+ first = getFirstQueuedThread(); // retry via getFirstQueuedThread
+ return first != null && first != Thread.currentThread();
}
// Instrumentation and monitoring methods
@@ -1098,7 +878,7 @@
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
- if (p.thread != null)
+ if (p.waiter != null)
++n;
}
return n;
@@ -1118,7 +898,7 @@
public final Collection<Thread> getQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- Thread t = p.thread;
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1136,8 +916,8 @@
public final Collection<Thread> getExclusiveQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- if (!p.isShared()) {
- Thread t = p.thread;
+ if (!(p instanceof SharedNode)) {
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1156,8 +936,8 @@
public final Collection<Thread> getSharedQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- if (p.isShared()) {
- Thread t = p.thread;
+ if (p instanceof SharedNode) {
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1180,117 +960,6 @@
+ (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
-
- // Internal support methods for Conditions
-
- /**
- * Returns true if a node, always one that was initially placed on
- * a condition queue, is now waiting to reacquire on sync queue.
- * @param node the node
- * @return true if is reacquiring
- */
- final boolean isOnSyncQueue(Node node) {
- if (node.waitStatus == Node.CONDITION || node.prev == null)
- return false;
- if (node.next != null) // If has successor, it must be on queue
- return true;
- /*
- * node.prev can be non-null, but not yet on queue because
- * the CAS to place it on queue can fail. So we have to
- * traverse from tail to make sure it actually made it. It
- * will always be near the tail in calls to this method, and
- * unless the CAS failed (which is unlikely), it will be
- * there, so we hardly ever traverse much.
- */
- return findNodeFromTail(node);
- }
-
- /**
- * Returns true if node is on sync queue by searching backwards from tail.
- * Called only when needed by isOnSyncQueue.
- * @return true if present
- */
- private boolean findNodeFromTail(Node node) {
- // We check for node first, since it's likely to be at or near tail.
- // tail is known to be non-null, so we could re-order to "save"
- // one null check, but we leave it this way to help the VM.
- for (Node p = tail;;) {
- if (p == node)
- return true;
- if (p == null)
- return false;
- p = p.prev;
- }
- }
-
- /**
- * Transfers a node from a condition queue onto sync queue.
- * Returns true if successful.
- * @param node the node
- * @return true if successfully transferred (else the node was
- * cancelled before signal)
- */
- final boolean transferForSignal(Node node) {
- /*
- * If cannot change waitStatus, the node has been cancelled.
- */
- if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
- return false;
-
- /*
- * Splice onto queue and try to set waitStatus of predecessor to
- * indicate that thread is (probably) waiting. If cancelled or
- * attempt to set waitStatus fails, wake up to resync (in which
- * case the waitStatus can be transiently and harmlessly wrong).
- */
- Node p = enq(node);
- int ws = p.waitStatus;
- if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
- LockSupport.unpark(node.thread);
- return true;
- }
-
- /**
- * Transfers node, if necessary, to sync queue after a cancelled wait.
- * Returns true if thread was cancelled before being signalled.
- *
- * @param node the node
- * @return true if cancelled before the node was signalled
- */
- final boolean transferAfterCancelledWait(Node node) {
- if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
- enq(node);
- return true;
- }
- /*
- * If we lost out to a signal(), then we can't proceed
- * until it finishes its enq(). Cancelling during an
- * incomplete transfer is both rare and transient, so just
- * spin.
- */
- while (!isOnSyncQueue(node))
- Thread.yield();
- return false;
- }
-
- /**
- * Invokes release with current state value; returns saved state.
- * Cancels node and throws exception on failure.
- * @param node the condition node for this wait
- * @return previous sync state
- */
- final long fullyRelease(Node node) {
- try {
- long savedState = getState();
- if (release(savedState))
- return savedState;
- throw new IllegalMonitorStateException();
- } catch (Throwable t) {
- node.waitStatus = Node.CANCELLED;
- throw t;
- }
- }
-
// Instrumentation methods for conditions
/**
@@ -1384,112 +1053,38 @@
*
* <p>This class is Serializable, but all fields are transient,
* so deserialized conditions have no waiters.
- *
- * @since 1.6
*/
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
- private transient Node firstWaiter;
+ private transient ConditionNode firstWaiter;
/** Last node of condition queue. */
- private transient Node lastWaiter;
+ private transient ConditionNode lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
- // Internal methods
-
- /**
- * Adds a new waiter to wait queue.
- * @return its new wait node
- */
- private Node addConditionWaiter() {
- if (!isHeldExclusively())
- throw new IllegalMonitorStateException();
- Node t = lastWaiter;
- // If lastWaiter is cancelled, clean out.
- if (t != null && t.waitStatus != Node.CONDITION) {
- unlinkCancelledWaiters();
- t = lastWaiter;
- }
-
- Node node = new Node(Node.CONDITION);
-
- if (t == null)
- firstWaiter = node;
- else
- t.nextWaiter = node;
- lastWaiter = node;
- return node;
- }
-
- /**
- * Removes and transfers nodes until hit non-cancelled one or
- * null. Split out from signal in part to encourage compilers
- * to inline the case of no waiters.
- * @param first (non-null) the first node on condition queue
- */
- private void doSignal(Node first) {
- do {
- if ( (firstWaiter = first.nextWaiter) == null)
- lastWaiter = null;
- first.nextWaiter = null;
- } while (!transferForSignal(first) &&
- (first = firstWaiter) != null);
- }
+ // Signalling methods
/**
- * Removes and transfers all nodes.
- * @param first (non-null) the first node on condition queue
+ * Removes and transfers one or all waiters to sync queue.
*/
- private void doSignalAll(Node first) {
- lastWaiter = firstWaiter = null;
- do {
- Node next = first.nextWaiter;
- first.nextWaiter = null;
- transferForSignal(first);
+ private void doSignal(ConditionNode first, boolean all) {
+ while (first != null) {
+ ConditionNode next = first.nextWaiter;
+ if ((firstWaiter = next) == null)
+ lastWaiter = null;
+ if ((first.getAndUnsetStatus(COND) & COND) != 0) {
+ enqueue(first);
+ if (!all)
+ break;
+ }
first = next;
- } while (first != null);
- }
-
- /**
- * Unlinks cancelled waiter nodes from condition queue.
- * Called only while holding lock. This is called when
- * cancellation occurred during condition wait, and upon
- * insertion of a new waiter when lastWaiter is seen to have
- * been cancelled. This method is needed to avoid garbage
- * retention in the absence of signals. So even though it may
- * require a full traversal, it comes into play only when
- * timeouts or cancellations occur in the absence of
- * signals. It traverses all nodes rather than stopping at a
- * particular target to unlink all pointers to garbage nodes
- * without requiring many re-traversals during cancellation
- * storms.
- */
- private void unlinkCancelledWaiters() {
- Node t = firstWaiter;
- Node trail = null;
- while (t != null) {
- Node next = t.nextWaiter;
- if (t.waitStatus != Node.CONDITION) {
- t.nextWaiter = null;
- if (trail == null)
- firstWaiter = next;
- else
- trail.nextWaiter = next;
- if (next == null)
- lastWaiter = trail;
- }
- else
- trail = t;
- t = next;
}
}
- // public methods
-
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
@@ -1499,11 +1094,11 @@
* returns {@code false}
*/
public final void signal() {
+ ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- Node first = firstWaiter;
if (first != null)
- doSignal(first);
+ doSignal(first, false);
}
/**
@@ -1514,11 +1109,72 @@
* returns {@code false}
*/
public final void signalAll() {
+ ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- Node first = firstWaiter;
if (first != null)
- doSignalAll(first);
+ doSignal(first, true);
+ }
+
+ // Waiting methods
+
+ /**
+ * Adds node to condition list and releases lock.
+ *
+ * @param node the node
+ * @return savedState to reacquire after wait
+ */
+ private long enableWait(ConditionNode node) {
+ if (isHeldExclusively()) {
+ node.waiter = Thread.currentThread();
+ node.setStatusRelaxed(COND | WAITING);
+ ConditionNode last = lastWaiter;
+ if (last == null)
+ firstWaiter = node;
+ else
+ last.nextWaiter = node;
+ lastWaiter = node;
+ long savedState = getState();
+ if (release(savedState))
+ return savedState;
+ }
+ node.status = CANCELLED; // lock not held or inconsistent
+ throw new IllegalMonitorStateException();
+ }
+
+ /**
+ * Returns true if a node that was initially placed on a condition
+ * queue is now ready to reacquire on sync queue.
+ * @param node the node
+ * @return true if is reacquiring
+ */
+ private boolean canReacquire(ConditionNode node) {
+ // check links, not status to avoid enqueue race
+ return node != null && node.prev != null && isEnqueued(node);
+ }
+
+ /**
+ * Unlinks the given node and other non-waiting nodes from
+ * condition queue unless already unlinked.
+ */
+ private void unlinkCancelledWaiters(ConditionNode node) {
+ if (node == null || node.nextWaiter != null || node == lastWaiter) {
+ ConditionNode w = firstWaiter, trail = null;
+ while (w != null) {
+ ConditionNode next = w.nextWaiter;
+ if ((w.status & COND) == 0) {
+ w.nextWaiter = null;
+ if (trail == null)
+ firstWaiter = next;
+ else
+ trail.nextWaiter = next;
+ if (next == null)
+ lastWaiter = trail;
+ } else
+ trail = w;
+ w = next;
+ }
+ }
}
/**
@@ -1533,51 +1189,27 @@
* </ol>
*/
public final void awaitUninterruptibly() {
- Node node = addConditionWaiter();
- long savedState = fullyRelease(node);
+ ConditionNode node = new ConditionNode();
+ long savedState = enableWait(node);
+ LockSupport.setCurrentBlocker(this); // for back-compatibility
boolean interrupted = false;
- while (!isOnSyncQueue(node)) {
- LockSupport.park(this);
+ while (!canReacquire(node)) {
if (Thread.interrupted())
interrupted = true;
+ else if ((node.status & COND) != 0) {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ } else
+ Thread.onSpinWait(); // awoke while enqueuing
}
- if (acquireQueued(node, savedState) || interrupted)
- selfInterrupt();
- }
-
- /*
- * For interruptible waits, we need to track whether to throw
- * InterruptedException, if interrupted while blocked on
- * condition, versus reinterrupt current thread, if
- * interrupted while blocked waiting to re-acquire.
- */
-
- /** Mode meaning to reinterrupt on exit from wait */
- private static final int REINTERRUPT = 1;
- /** Mode meaning to throw InterruptedException on exit from wait */
- private static final int THROW_IE = -1;
-
- /**
- * Checks for interrupt, returning THROW_IE if interrupted
- * before signalled, REINTERRUPT if after signalled, or
- * 0 if not interrupted.
- */
- private int checkInterruptWhileWaiting(Node node) {
- return Thread.interrupted() ?
- (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
- 0;
- }
-
- /**
- * Throws InterruptedException, reinterrupts current thread, or
- * does nothing, depending on mode.
- */
- private void reportInterruptAfterWait(int interruptMode)
- throws InterruptedException {
- if (interruptMode == THROW_IE)
- throw new InterruptedException();
- else if (interruptMode == REINTERRUPT)
- selfInterrupt();
+ LockSupport.setCurrentBlocker(null);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (interrupted)
+ Thread.currentThread().interrupt();
}
/**
@@ -1596,20 +1228,33 @@
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
- Node node = addConditionWaiter();
- long savedState = fullyRelease(node);
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- LockSupport.park(this);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
+ ConditionNode node = new ConditionNode();
+ long savedState = enableWait(node);
+ LockSupport.setCurrentBlocker(this); // for back-compatibility
+ boolean interrupted = false, cancelled = false;
+ while (!canReacquire(node)) {
+ if (interrupted |= Thread.interrupted()) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break; // else interrupted after signal
+ } else if ((node.status & COND) != 0) {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ } else
+ Thread.onSpinWait(); // awoke while enqueuing
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null) // clean up if cancelled
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
+ LockSupport.setCurrentBlocker(null);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (interrupted) {
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ throw new InterruptedException();
+ }
+ Thread.currentThread().interrupt();
+ }
}
/**
@@ -1629,32 +1274,29 @@
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
- // We don't check for nanosTimeout <= 0L here, to allow
- // awaitNanos(0) as a way to "yield the lock".
- final long deadline = System.nanoTime() + nanosTimeout;
- long initialNanos = nanosTimeout;
- Node node = addConditionWaiter();
- long savedState = fullyRelease(node);
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (nanosTimeout <= 0L) {
- transferAfterCancelledWait(node);
- break;
- }
- if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
- nanosTimeout = deadline - System.nanoTime();
+ ConditionNode node = new ConditionNode();
+ long savedState = enableWait(node);
+ long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
+ long deadline = System.nanoTime() + nanos;
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ (nanos = deadline - System.nanoTime()) <= 0L) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkNanos(this, nanos);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
long remaining = deadline - System.nanoTime(); // avoid overflow
- return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
+ return (remaining <= nanosTimeout) ? remaining : Long.MIN_VALUE;
}
/**
@@ -1676,26 +1318,26 @@
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
- Node node = addConditionWaiter();
- long savedState = fullyRelease(node);
- boolean timedout = false;
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() >= abstime) {
- timedout = transferAfterCancelledWait(node);
- break;
- }
- LockSupport.parkUntil(this, abstime);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
+ ConditionNode node = new ConditionNode();
+ long savedState = enableWait(node);
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ System.currentTimeMillis() >= abstime) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkUntil(this, abstime);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
- return !timedout;
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
+ return !cancelled;
}
/**
@@ -1717,31 +1359,28 @@
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
- // We don't check for nanosTimeout <= 0L here, to allow
- // await(0, unit) as a way to "yield the lock".
- final long deadline = System.nanoTime() + nanosTimeout;
- Node node = addConditionWaiter();
- long savedState = fullyRelease(node);
- boolean timedout = false;
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (nanosTimeout <= 0L) {
- timedout = transferAfterCancelledWait(node);
- break;
- }
- if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
- nanosTimeout = deadline - System.nanoTime();
+ ConditionNode node = new ConditionNode();
+ long savedState = enableWait(node);
+ long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
+ long deadline = System.nanoTime() + nanos;
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ (nanos = deadline - System.nanoTime()) <= 0L) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkNanos(this, nanos);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
- return !timedout;
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
+ return !cancelled;
}
// support for instrumentation
@@ -1767,8 +1406,8 @@
protected final boolean hasWaiters() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION)
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0)
return true;
}
return false;
@@ -1787,8 +1426,8 @@
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION)
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0)
++n;
}
return n;
@@ -1807,9 +1446,9 @@
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
ArrayList<Thread> list = new ArrayList<>();
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION) {
- Thread t = w.thread;
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0) {
+ Thread t = w.waiter;
if (t != null)
list.add(t);
}
@@ -1818,39 +1457,16 @@
}
}
- // VarHandle mechanics
- private static final VarHandle STATE;
- private static final VarHandle HEAD;
- private static final VarHandle TAIL;
+ // Unsafe
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final long STATE
+ = U.objectFieldOffset(AbstractQueuedLongSynchronizer.class, "state");
+ private static final long HEAD
+ = U.objectFieldOffset(AbstractQueuedLongSynchronizer.class, "head");
+ private static final long TAIL
+ = U.objectFieldOffset(AbstractQueuedLongSynchronizer.class, "tail");
static {
- try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATE = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "state", long.class);
- HEAD = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "head", Node.class);
- TAIL = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "tail", Node.class);
- } catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
- }
-
- // Reduce the risk of rare disastrous classloading in first call to
- // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
Class<?> ensureLoaded = LockSupport.class;
}
-
- /**
- * Initializes head and tail fields on first contention.
- */
- private final void initializeSyncQueue() {
- Node h;
- if (HEAD.compareAndSet(this, null, (h = new Node())))
- tail = h;
- }
-
- /**
- * CASes tail field.
- */
- private final boolean compareAndSetTail(Node expect, Node update) {
- return TAIL.compareAndSet(this, expect, update);
- }
}
--- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Wed Sep 18 07:46:02 2019 +0200
@@ -35,12 +35,12 @@
package java.util.concurrent.locks;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ForkJoinPool;
+import jdk.internal.misc.Unsafe;
/**
* Provides a framework for implementing blocking locks and related
@@ -312,265 +312,208 @@
*/
protected AbstractQueuedSynchronizer() { }
- /**
- * Wait queue node class.
+ /*
+ * Overview.
*
- * <p>The wait queue is a variant of a "CLH" (Craig, Landin, and
+ * The wait queue is a variant of a "CLH" (Craig, Landin, and
* Hagersten) lock queue. CLH locks are normally used for
- * spinlocks. We instead use them for blocking synchronizers, but
- * use the same basic tactic of holding some of the control
- * information about a thread in the predecessor of its node. A
- * "status" field in each node keeps track of whether a thread
- * should block. A node is signalled when its predecessor
- * releases. Each node of the queue otherwise serves as a
- * specific-notification-style monitor holding a single waiting
- * thread. The status field does NOT control whether threads are
- * granted locks etc though. A thread may try to acquire if it is
- * first in the queue. But being first does not guarantee success;
- * it only gives the right to contend. So the currently released
- * contender thread may need to rewait.
+ * spinlocks. We instead use them for blocking synchronizers by
+ * including explicit ("prev" and "next") links plus a "status"
+ * field that allow nodes to signal successors when releasing
+ * locks, and handle cancellation due to interrupts and timeouts.
+ * The status field includes bits that track whether a thread
+ * needs a signal (using LockSupport.unpark). Despite these
+ * additions, we maintain most CLH locality properties.
+ *
+ * To enqueue into a CLH lock, you atomically splice it in as new
+ * tail. To dequeue, you set the head field, so the next eligible
+ * waiter becomes first.
*
- * <p>To enqueue into a CLH lock, you atomically splice it in as new
- * tail. To dequeue, you just set the head field.
- * <pre>
- * +------+ prev +-----+ +-----+
- * head | | <---- | | <---- | | tail
- * +------+ +-----+ +-----+
- * </pre>
+ * +------+ prev +-------+ +------+
+ * | head | <---- | first | <---- | tail |
+ * +------+ +-------+ +------+
+ *
+ * Insertion into a CLH queue requires only a single atomic
+ * operation on "tail", so there is a simple point of demarcation
+ * from unqueued to queued. The "next" link of the predecessor is
+ * set by the enqueuing thread after successful CAS. Even though
+ * non-atomic, this suffices to ensure that any blocked thread is
+ * signalled by a predecessor when eligible (although in the case
+ * of cancellation, possibly with the assistance of a signal in
+ * method cleanQueue). Signalling is based in part on a
+ * Dekker-like scheme in which the to-be waiting thread indicates
+ * WAITING status, then retries acquiring, and then rechecks
+ * status before blocking. The signaller atomically clears WAITING
+ * status when unparking.
*
- * <p>Insertion into a CLH queue requires only a single atomic
- * operation on "tail", so there is a simple atomic point of
- * demarcation from unqueued to queued. Similarly, dequeuing
- * involves only updating the "head". However, it takes a bit
- * more work for nodes to determine who their successors are,
- * in part to deal with possible cancellation due to timeouts
- * and interrupts.
- *
- * <p>The "prev" links (not used in original CLH locks), are mainly
- * needed to handle cancellation. If a node is cancelled, its
- * successor is (normally) relinked to a non-cancelled
- * predecessor. For explanation of similar mechanics in the case
- * of spin locks, see the papers by Scott and Scherer at
- * http://www.cs.rochester.edu/u/scott/synchronization/
+ * Dequeuing on acquire involves detaching (nulling) a node's
+ * "prev" node and then updating the "head". Other threads check
+ * if a node is or was dequeued by checking "prev" rather than
+ * head. We enforce the nulling then setting order by spin-waiting
+ * if necessary. Because of this, the lock algorithm is not itself
+ * strictly "lock-free" because an acquiring thread may need to
+ * wait for a previous acquire to make progress. When used with
+ * exclusive locks, such progress is required anyway. However
+ * Shared mode may (uncommonly) require a spin-wait before
+ * setting head field to ensure proper propagation. (Historical
+ * note: This allows some simplifications and efficiencies
+ * compared to previous versions of this class.)
*
- * <p>We also use "next" links to implement blocking mechanics.
- * The thread id for each node is kept in its own node, so a
- * predecessor signals the next node to wake up by traversing
- * next link to determine which thread it is. Determination of
- * successor must avoid races with newly queued nodes to set
- * the "next" fields of their predecessors. This is solved
- * when necessary by checking backwards from the atomically
- * updated "tail" when a node's successor appears to be null.
- * (Or, said differently, the next-links are an optimization
- * so that we don't usually need a backward scan.)
+ * A node's predecessor can change due to cancellation while it is
+ * waiting, until the node is first in queue, at which point it
+ * cannot change. The acquire methods cope with this by rechecking
+ * "prev" before waiting. The prev and next fields are modified
+ * only via CAS by cancelled nodes in method cleanQueue. The
+ * unsplice strategy is reminiscent of Michael-Scott queues in
+ * that after a successful CAS to prev field, other threads help
+ * fix next fields. Because cancellation often occurs in bunches
+ * that complicate decisions about necessary signals, each call to
+ * cleanQueue traverses the queue until a clean sweep. Nodes that
+ * become relinked as first are unconditionally unparked
+ * (sometimes unnecessarily, but those cases are not worth
+ * avoiding).
*
- * <p>Cancellation introduces some conservatism to the basic
- * algorithms. Since we must poll for cancellation of other
- * nodes, we can miss noticing whether a cancelled node is
- * ahead or behind us. This is dealt with by always unparking
- * successors upon cancellation, allowing them to stabilize on
- * a new predecessor, unless we can identify an uncancelled
- * predecessor who will carry this responsibility.
+ * A thread may try to acquire if it is first (frontmost) in the
+ * queue, and sometimes before. Being first does not guarantee
+ * success; it only gives the right to contend. We balance
+ * throughput, overhead, and fairness by allowing incoming threads
+ * to "barge" and acquire the synchronizer while in the process of
+ * enqueuing, in which case an awakened first thread may need to
+ * rewait. To counteract possible repeated unlucky rewaits, we
+ * exponentially increase retries (up to 256) to acquire each time
+ * a thread is unparked. Except in this case, AQS locks do not
+ * spin; they instead interleave attempts to acquire with
+ * bookkeeping steps. (Users who want spinlocks can use
+ * tryAcquire.)
*
- * <p>CLH queues need a dummy header node to get started. But
+ * To improve garbage collectibility, fields of nodes not yet on
+ * list are null. (It is not rare to create and then throw away a
+ * node without using it.) Fields of nodes coming off the list are
+ * nulled out as soon as possible. This accentuates the challenge
+ * of externally determining the first waiting thread (as in
+ * method getFirstQueuedThread). This sometimes requires the
+ * fallback of traversing backwards from the atomically updated
+ * "tail" when fields appear null. (This is never needed in the
+ * process of signalling though.)
+ *
+ * CLH queues need a dummy header node to get started. But
* we don't create them on construction, because it would be wasted
* effort if there is never contention. Instead, the node
* is constructed and head and tail pointers are set upon first
* contention.
*
- * <p>Threads waiting on Conditions use the same nodes, but
- * use an additional link. Conditions only need to link nodes
- * in simple (non-concurrent) linked queues because they are
- * only accessed when exclusively held. Upon await, a node is
- * inserted into a condition queue. Upon signal, the node is
- * transferred to the main queue. A special value of status
- * field is used to mark which queue a node is on.
+ * Shared mode operations differ from Exclusive in that an acquire
+ * signals the next waiter to try to acquire if it is also
+ * Shared. The tryAcquireShared API allows users to indicate the
+ * degree of propagation, but in most applications, it is more
+ * efficient to ignore this, allowing the successor to try
+ * acquiring in any case.
+ *
+ * Threads waiting on Conditions use nodes with an additional
+ * link to maintain the (FIFO) list of conditions. Conditions only
+ * need to link nodes in simple (non-concurrent) linked queues
+ * because they are only accessed when exclusively held. Upon
+ * await, a node is inserted into a condition queue. Upon signal,
+ * the node is enqueued on the main queue. A special status field
+ * value is used to track and atomically trigger this.
*
- * <p>Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
+ * Accesses to fields head, tail, and state use full Volatile
+ * mode, along with CAS. Node fields status, prev and next also do
+ * so while threads may be signallable, but sometimes use weaker
+ * modes otherwise. Accesses to field "waiter" (the thread to be
+ * signalled) are always sandwiched between other atomic accesses
+ * so are used in Plain mode. We use jdk.internal Unsafe versions
+ * of atomic access methods rather than VarHandles to avoid
+ * potential VM bootstrap issues.
+ *
+ * Most of the above is performed by primary internal method
+ * acquire, that is invoked in some way by all exported acquire
+ * methods. (It is usually easy for compilers to optimize
+ * call-site specializations when heavily used.)
+ *
+ * There are several arbitrary decisions about when and how to
+ * check interrupts in both acquire and await before and/or after
+ * blocking. The decisions are less arbitrary in implementation
+ * updates because some users appear to rely on original behaviors
+ * in ways that are racy and so (rarely) wrong in general but hard
+ * to justify changing.
+ *
+ * Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
* Scherer and Michael Scott, along with members of JSR-166
* expert group, for helpful ideas, discussions, and critiques
* on the design of this class.
*/
- static final class Node {
- /** Marker to indicate a node is waiting in shared mode */
- static final Node SHARED = new Node();
- /** Marker to indicate a node is waiting in exclusive mode */
- static final Node EXCLUSIVE = null;
+
+ // Node status bits, also used as argument and return values
+ static final int WAITING = 1; // must be 1
+ static final int CANCELLED = 0x80000000; // must be negative
+ static final int COND = 2; // in a condition wait
- /** waitStatus value to indicate thread has cancelled. */
- static final int CANCELLED = 1;
- /** waitStatus value to indicate successor's thread needs unparking. */
- static final int SIGNAL = -1;
- /** waitStatus value to indicate thread is waiting on condition. */
- static final int CONDITION = -2;
- /**
- * waitStatus value to indicate the next acquireShared should
- * unconditionally propagate.
- */
- static final int PROPAGATE = -3;
+ /** CLH Nodes */
+ abstract static class Node {
+ volatile Node prev; // initially attached via casTail
+ volatile Node next; // visibly nonnull when signallable
+ Thread waiter; // visibly nonnull when enqueued
+ volatile int status; // written by owner, atomic bit ops by others
- /**
- * Status field, taking on only the values:
- * SIGNAL: The successor of this node is (or will soon be)
- * blocked (via park), so the current node must
- * unpark its successor when it releases or
- * cancels. To avoid races, acquire methods must
- * first indicate they need a signal,
- * then retry the atomic acquire, and then,
- * on failure, block.
- * CANCELLED: This node is cancelled due to timeout or interrupt.
- * Nodes never leave this state. In particular,
- * a thread with cancelled node never again blocks.
- * CONDITION: This node is currently on a condition queue.
- * It will not be used as a sync queue node
- * until transferred, at which time the status
- * will be set to 0. (Use of this value here has
- * nothing to do with the other uses of the
- * field, but simplifies mechanics.)
- * PROPAGATE: A releaseShared should be propagated to other
- * nodes. This is set (for head node only) in
- * doReleaseShared to ensure propagation
- * continues, even if other operations have
- * since intervened.
- * 0: None of the above
- *
- * The values are arranged numerically to simplify use.
- * Non-negative values mean that a node doesn't need to
- * signal. So, most code doesn't need to check for particular
- * values, just for sign.
- *
- * The field is initialized to 0 for normal sync nodes, and
- * CONDITION for condition nodes. It is modified using CAS
- * (or when possible, unconditional volatile writes).
- */
- volatile int waitStatus;
+ // methods for atomic operations
+ final boolean casPrev(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, PREV, c, v);
+ }
+ final boolean casNext(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, NEXT, c, v);
+ }
+ final int getAndUnsetStatus(int v) { // for signalling
+ return U.getAndBitwiseAndInt(this, STATUS, ~v);
+ }
+ final void setPrevRelaxed(Node p) { // for off-queue assignment
+ U.putReference(this, PREV, p);
+ }
+ final void setStatusRelaxed(int s) { // for off-queue assignment
+ U.putInt(this, STATUS, s);
+ }
+ final void clearStatus() { // for reducing unneeded signals
+ U.putIntOpaque(this, STATUS, 0);
+ }
- /**
- * Link to predecessor node that current node/thread relies on
- * for checking waitStatus. Assigned during enqueuing, and nulled
- * out (for sake of GC) only upon dequeuing. Also, upon
- * cancellation of a predecessor, we short-circuit while
- * finding a non-cancelled one, which will always exist
- * because the head node is never cancelled: A node becomes
- * head only as a result of successful acquire. A
- * cancelled thread never succeeds in acquiring, and a thread only
- * cancels itself, not any other node.
- */
- volatile Node prev;
+ private static final long STATUS
+ = U.objectFieldOffset(Node.class, "status");
+ private static final long NEXT
+ = U.objectFieldOffset(Node.class, "next");
+ private static final long PREV
+ = U.objectFieldOffset(Node.class, "prev");
+ }
- /**
- * Link to the successor node that the current node/thread
- * unparks upon release. Assigned during enqueuing, adjusted
- * when bypassing cancelled predecessors, and nulled out (for
- * sake of GC) when dequeued. The enq operation does not
- * assign next field of a predecessor until after attachment,
- * so seeing a null next field does not necessarily mean that
- * node is at end of queue. However, if a next field appears
- * to be null, we can scan prev's from the tail to
- * double-check. The next field of cancelled nodes is set to
- * point to the node itself instead of null, to make life
- * easier for isOnSyncQueue.
- */
- volatile Node next;
+ // Concrete classes tagged by type
+ static final class ExclusiveNode extends Node { }
+ static final class SharedNode extends Node { }
+
+ static final class ConditionNode extends Node
+ implements ForkJoinPool.ManagedBlocker {
+ ConditionNode nextWaiter; // link to next waiting node
/**
- * The thread that enqueued this node. Initialized on
- * construction and nulled out after use.
- */
- volatile Thread thread;
-
- /**
- * Link to next node waiting on condition, or the special
- * value SHARED. Because condition queues are accessed only
- * when holding in exclusive mode, we just need a simple
- * linked queue to hold nodes while they are waiting on
- * conditions. They are then transferred to the queue to
- * re-acquire. And because conditions can only be exclusive,
- * we save a field by using special value to indicate shared
- * mode.
+ * Allows Conditions to be used in ForkJoinPools without
+ * risking fixed pool exhaustion. This is usable only for
+ * untimed Condition waits, not timed versions.
*/
- Node nextWaiter;
-
- /**
- * Returns true if node is waiting in shared mode.
- */
- final boolean isShared() {
- return nextWaiter == SHARED;
- }
-
- /**
- * Returns previous node, or throws NullPointerException if null.
- * Use when predecessor cannot be null. The null check could
- * be elided, but is present to help the VM.
- *
- * @return the predecessor of this node
- */
- final Node predecessor() {
- Node p = prev;
- if (p == null)
- throw new NullPointerException();
- else
- return p;
+ public final boolean isReleasable() {
+ return status <= 1 || Thread.currentThread().isInterrupted();
}
- /** Establishes initial head or SHARED marker. */
- Node() {}
-
- /** Constructor used by addWaiter. */
- Node(Node nextWaiter) {
- this.nextWaiter = nextWaiter;
- THREAD.set(this, Thread.currentThread());
- }
-
- /** Constructor used by addConditionWaiter. */
- Node(int waitStatus) {
- WAITSTATUS.set(this, waitStatus);
- THREAD.set(this, Thread.currentThread());
- }
-
- /** CASes waitStatus field. */
- final boolean compareAndSetWaitStatus(int expect, int update) {
- return WAITSTATUS.compareAndSet(this, expect, update);
- }
-
- /** CASes next field. */
- final boolean compareAndSetNext(Node expect, Node update) {
- return NEXT.compareAndSet(this, expect, update);
- }
-
- final void setPrevRelaxed(Node p) {
- PREV.set(this, p);
- }
-
- // VarHandle mechanics
- private static final VarHandle NEXT;
- private static final VarHandle PREV;
- private static final VarHandle THREAD;
- private static final VarHandle WAITSTATUS;
- static {
- try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- NEXT = l.findVarHandle(Node.class, "next", Node.class);
- PREV = l.findVarHandle(Node.class, "prev", Node.class);
- THREAD = l.findVarHandle(Node.class, "thread", Thread.class);
- WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class);
- } catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
- }
+ public final boolean block() {
+ while (!isReleasable()) LockSupport.park(this);
+ return true;
}
}
/**
- * Head of the wait queue, lazily initialized. Except for
- * initialization, it is modified only via method setHead. Note:
- * If head exists, its waitStatus is guaranteed not to be
- * CANCELLED.
+ * Head of the wait queue, lazily initialized.
*/
private transient volatile Node head;
/**
- * Tail of the wait queue, lazily initialized. Modified only via
- * method enq to add new wait node.
+ * Tail of the wait queue. After initialization, modified only via casTail.
*/
private transient volatile Node tail;
@@ -609,481 +552,235 @@
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
- return STATE.compareAndSet(this, expect, update);
+ return U.compareAndSetInt(this, STATE, expect, update);
}
// Queuing utilities
- /**
- * The number of nanoseconds for which it is faster to spin
- * rather than to use timed park. A rough estimate suffices
- * to improve responsiveness with very short timeouts.
- */
- static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
+ private boolean casTail(Node c, Node v) {
+ return U.compareAndSetReference(this, TAIL, c, v);
+ }
+
+ /** tries once to CAS a new dummy node for head */
+ private void tryInitializeHead() {
+ Node h = new ExclusiveNode();
+ if (U.compareAndSetReference(this, HEAD, null, h))
+ tail = h;
+ }
/**
- * Inserts node into queue, initializing if necessary. See picture above.
- * @param node the node to insert
- * @return node's predecessor
+ * Enqueues the node unless null. (Currently used only for
+ * ConditionNodes; other cases are interleaved with acquires.)
*/
- private Node enq(Node node) {
- for (;;) {
- Node oldTail = tail;
- if (oldTail != null) {
- node.setPrevRelaxed(oldTail);
- if (compareAndSetTail(oldTail, node)) {
- oldTail.next = node;
- return oldTail;
+ final void enqueue(Node node) {
+ if (node != null) {
+ for (;;) {
+ Node t = tail;
+ node.setPrevRelaxed(t); // avoid unnecessary fence
+ if (t == null) // initialize
+ tryInitializeHead();
+ else if (casTail(t, node)) {
+ t.next = node;
+ if (t.status < 0) // wake up to clean link
+ LockSupport.unpark(node.waiter);
+ break;
}
- } else {
- initializeSyncQueue();
}
}
}
+ /** Returns true if node is found in traversal from tail */
+ final boolean isEnqueued(Node node) {
+ for (Node t = tail; t != null; t = t.prev)
+ if (t == node)
+ return true;
+ return false;
+ }
+
/**
- * Creates and enqueues node for current thread and given mode.
+ * Wakes up the successor of given node, if one exists, and unsets its
+ * WAITING status to avoid park race. This may fail to wake up an
+ * eligible thread when one or more have been cancelled, but
+ * cancelAcquire ensures liveness.
+ */
+ private static void signalNext(Node h) {
+ Node s;
+ if (h != null && (s = h.next) != null && s.status != 0) {
+ s.getAndUnsetStatus(WAITING);
+ LockSupport.unpark(s.waiter);
+ }
+ }
+
+ /** Wakes up the given node if in shared mode */
+ private static void signalNextIfShared(Node h) {
+ Node s;
+ if (h != null && (s = h.next) != null &&
+ (s instanceof SharedNode) && s.status != 0) {
+ s.getAndUnsetStatus(WAITING);
+ LockSupport.unpark(s.waiter);
+ }
+ }
+
+ /**
+ * Main acquire method, invoked by all exported acquire methods.
*
- * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
- * @return the new node
+ * @param node null unless a reacquiring Condition
+ * @param arg the acquire argument
+ * @param shared true if shared mode else exclusive
+ * @param interruptible if abort and return negative on interrupt
+ * @param timed if true use timed waits
+ * @param time if timed, the System.nanoTime value to timeout
+ * @return positive if acquired, 0 if timed out, negative if interrupted
*/
- private Node addWaiter(Node mode) {
- Node node = new Node(mode);
+ final int acquire(Node node, int arg, boolean shared,
+ boolean interruptible, boolean timed, long time) {
+ Thread current = Thread.currentThread();
+ byte spins = 0, postSpins = 0; // retries upon unpark of first thread
+ boolean interrupted = false, first = false;
+ Node pred = null; // predecessor of node when enqueued
+
+ /*
+ * Repeatedly:
+ * Check if node now first
+ * if so, ensure head stable, else ensure valid predecessor
+ * if node is first or not yet enqueued, try acquiring
+ * else if node not yet created, create it
+ * else if not yet enqueued, try once to enqueue
+ * else if woken from park, retry (up to postSpins times)
+ * else if WAITING status not set, set and retry
+ * else park and clear WAITING status, and check cancellation
+ */
for (;;) {
- Node oldTail = tail;
- if (oldTail != null) {
- node.setPrevRelaxed(oldTail);
- if (compareAndSetTail(oldTail, node)) {
- oldTail.next = node;
- return node;
+ if (!first && (pred = (node == null) ? null : node.prev) != null &&
+ !(first = (head == pred))) {
+ if (pred.status < 0) {
+ cleanQueue(); // predecessor cancelled
+ continue;
+ } else if (pred.prev == null) {
+ Thread.onSpinWait(); // ensure serialization
+ continue;
+ }
+ }
+ if (first || pred == null) {
+ boolean acquired;
+ try {
+ if (shared)
+ acquired = (tryAcquireShared(arg) >= 0);
+ else
+ acquired = tryAcquire(arg);
+ } catch (Throwable ex) {
+ cancelAcquire(node, interrupted, false);
+ throw ex;
+ }
+ if (acquired) {
+ if (first) {
+ node.prev = null;
+ head = node;
+ pred.next = null;
+ node.waiter = null;
+ if (shared)
+ signalNextIfShared(node);
+ if (interrupted)
+ current.interrupt();
+ }
+ return 1;
}
+ }
+ if (node == null) { // allocate; retry before enqueue
+ if (shared)
+ node = new SharedNode();
+ else
+ node = new ExclusiveNode();
+ } else if (pred == null) { // try to enqueue
+ node.waiter = current;
+ Node t = tail;
+ node.setPrevRelaxed(t); // avoid unnecessary fence
+ if (t == null)
+ tryInitializeHead();
+ else if (!casTail(t, node))
+ node.setPrevRelaxed(null); // back out
+ else
+ t.next = node;
+ } else if (first && spins != 0) {
+ --spins; // reduce unfairness on rewaits
+ Thread.onSpinWait();
+ } else if (node.status == 0) {
+ node.status = WAITING; // enable signal and recheck
} else {
- initializeSyncQueue();
+ long nanos;
+ spins = postSpins = (byte)((postSpins << 1) | 1);
+ if (!timed)
+ LockSupport.park(this);
+ else if ((nanos = time - System.nanoTime()) > 0L)
+ LockSupport.parkNanos(this, nanos);
+ else
+ break;
+ node.clearStatus();
+ if ((interrupted |= Thread.interrupted()) && interruptible)
+ break;
+ }
+ }
+ return cancelAcquire(node, interrupted, interruptible);
+ }
+
+ /**
+ * Possibly repeatedly traverses from tail, unsplicing cancelled
+ * nodes until none are found. Unparks nodes that may have been
+ * relinked to be next eligible acquirer.
+ */
+ private void cleanQueue() {
+ for (;;) { // restart point
+ for (Node q = tail, s = null, p, n;;) { // (p, q, s) triples
+ if (q == null || (p = q.prev) == null)
+ return; // end of list
+ if (s == null ? tail != q : (s.prev != q || s.status < 0))
+ break; // inconsistent
+ if (q.status < 0) { // cancelled
+ if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
+ q.prev == p) {
+ p.casNext(q, s); // OK if fails
+ if (p.prev == null)
+ signalNext(p);
+ }
+ break;
+ }
+ if ((n = p.next) != q) { // help finish
+ if (n != null && q.prev == p) {
+ p.casNext(n, q);
+ if (p.prev == null)
+ signalNext(p);
+ }
+ break;
+ }
+ s = q;
+ q = q.prev;
}
}
}
/**
- * Sets head of queue to be node, thus dequeuing. Called only by
- * acquire methods. Also nulls out unused fields for sake of GC
- * and to suppress unnecessary signals and traversals.
- *
- * @param node the node
- */
- private void setHead(Node node) {
- head = node;
- node.thread = null;
- node.prev = null;
- }
-
- /**
- * Wakes up node's successor, if one exists.
- *
- * @param node the node
- */
- private void unparkSuccessor(Node node) {
- /*
- * If status is negative (i.e., possibly needing signal) try
- * to clear in anticipation of signalling. It is OK if this
- * fails or if status is changed by waiting thread.
- */
- int ws = node.waitStatus;
- if (ws < 0)
- node.compareAndSetWaitStatus(ws, 0);
-
- /*
- * Thread to unpark is held in successor, which is normally
- * just the next node. But if cancelled or apparently null,
- * traverse backwards from tail to find the actual
- * non-cancelled successor.
- */
- Node s = node.next;
- if (s == null || s.waitStatus > 0) {
- s = null;
- for (Node p = tail; p != node && p != null; p = p.prev)
- if (p.waitStatus <= 0)
- s = p;
- }
- if (s != null)
- LockSupport.unpark(s.thread);
- }
-
- /**
- * Release action for shared mode -- signals successor and ensures
- * propagation. (Note: For exclusive mode, release just amounts
- * to calling unparkSuccessor of head if it needs signal.)
- */
- private void doReleaseShared() {
- /*
- * Ensure that a release propagates, even if there are other
- * in-progress acquires/releases. This proceeds in the usual
- * way of trying to unparkSuccessor of head if it needs
- * signal. But if it does not, status is set to PROPAGATE to
- * ensure that upon release, propagation continues.
- * Additionally, we must loop in case a new node is added
- * while we are doing this. Also, unlike other uses of
- * unparkSuccessor, we need to know if CAS to reset status
- * fails, if so rechecking.
- */
- for (;;) {
- Node h = head;
- if (h != null && h != tail) {
- int ws = h.waitStatus;
- if (ws == Node.SIGNAL) {
- if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
- continue; // loop to recheck cases
- unparkSuccessor(h);
- }
- else if (ws == 0 &&
- !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
- continue; // loop on failed CAS
- }
- if (h == head) // loop if head changed
- break;
- }
- }
-
- /**
- * Sets head of queue, and checks if successor may be waiting
- * in shared mode, if so propagating if either propagate > 0 or
- * PROPAGATE status was set.
- *
- * @param node the node
- * @param propagate the return value from a tryAcquireShared
- */
- private void setHeadAndPropagate(Node node, int propagate) {
- Node h = head; // Record old head for check below
- setHead(node);
- /*
- * Try to signal next queued node if:
- * Propagation was indicated by caller,
- * or was recorded (as h.waitStatus either before
- * or after setHead) by a previous operation
- * (note: this uses sign-check of waitStatus because
- * PROPAGATE status may transition to SIGNAL.)
- * and
- * The next node is waiting in shared mode,
- * or we don't know, because it appears null
- *
- * The conservatism in both of these checks may cause
- * unnecessary wake-ups, but only when there are multiple
- * racing acquires/releases, so most need signals now or soon
- * anyway.
- */
- if (propagate > 0 || h == null || h.waitStatus < 0 ||
- (h = head) == null || h.waitStatus < 0) {
- Node s = node.next;
- if (s == null || s.isShared())
- doReleaseShared();
- }
- }
-
- // Utilities for various versions of acquire
-
- /**
* Cancels an ongoing attempt to acquire.
*
- * @param node the node
- */
- private void cancelAcquire(Node node) {
- // Ignore if node doesn't exist
- if (node == null)
- return;
-
- node.thread = null;
-
- // Skip cancelled predecessors
- Node pred = node.prev;
- while (pred.waitStatus > 0)
- node.prev = pred = pred.prev;
-
- // predNext is the apparent node to unsplice. CASes below will
- // fail if not, in which case, we lost race vs another cancel
- // or signal, so no further action is necessary, although with
- // a possibility that a cancelled node may transiently remain
- // reachable.
- Node predNext = pred.next;
-
- // Can use unconditional write instead of CAS here.
- // After this atomic step, other Nodes can skip past us.
- // Before, we are free of interference from other threads.
- node.waitStatus = Node.CANCELLED;
-
- // If we are the tail, remove ourselves.
- if (node == tail && compareAndSetTail(node, pred)) {
- pred.compareAndSetNext(predNext, null);
- } else {
- // If successor needs signal, try to set pred's next-link
- // so it will get one. Otherwise wake it up to propagate.
- int ws;
- if (pred != head &&
- ((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
- pred.thread != null) {
- Node next = node.next;
- if (next != null && next.waitStatus <= 0)
- pred.compareAndSetNext(predNext, next);
- } else {
- unparkSuccessor(node);
- }
-
- node.next = node; // help GC
- }
- }
-
- /**
- * Checks and updates status for a node that failed to acquire.
- * Returns true if thread should block. This is the main signal
- * control in all acquire loops. Requires that pred == node.prev.
- *
- * @param pred node's predecessor holding status
- * @param node the node
- * @return {@code true} if thread should block
- */
- private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int ws = pred.waitStatus;
- if (ws == Node.SIGNAL)
- /*
- * This node has already set status asking a release
- * to signal it, so it can safely park.
- */
- return true;
- if (ws > 0) {
- /*
- * Predecessor was cancelled. Skip over predecessors and
- * indicate retry.
- */
- do {
- node.prev = pred = pred.prev;
- } while (pred.waitStatus > 0);
- pred.next = node;
- } else {
- /*
- * waitStatus must be 0 or PROPAGATE. Indicate that we
- * need a signal, but don't park yet. Caller will need to
- * retry to make sure it cannot acquire before parking.
- */
- pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
- }
- return false;
- }
-
- /**
- * Convenience method to interrupt current thread.
- */
- static void selfInterrupt() {
- Thread.currentThread().interrupt();
- }
-
- /**
- * Convenience method to park and then check if interrupted.
- *
- * @return {@code true} if interrupted
- */
- private final boolean parkAndCheckInterrupt() {
- LockSupport.park(this);
- return Thread.interrupted();
- }
-
- /*
- * Various flavors of acquire, varying in exclusive/shared and
- * control modes. Each is mostly the same, but annoyingly
- * different. Only a little bit of factoring is possible due to
- * interactions of exception mechanics (including ensuring that we
- * cancel if tryAcquire throws exception) and other control, at
- * least not without hurting performance too much.
- */
-
- /**
- * Acquires in exclusive uninterruptible mode for thread already in
- * queue. Used by condition wait methods as well as acquire.
- *
- * @param node the node
- * @param arg the acquire argument
- * @return {@code true} if interrupted while waiting
- */
- final boolean acquireQueued(final Node node, int arg) {
- boolean interrupted = false;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return interrupted;
- }
- if (shouldParkAfterFailedAcquire(p, node))
- interrupted |= parkAndCheckInterrupt();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- if (interrupted)
- selfInterrupt();
- throw t;
- }
- }
-
- /**
- * Acquires in exclusive interruptible mode.
- * @param arg the acquire argument
+ * @param node the node (may be null if cancelled before enqueuing)
+ * @param interrupted true if thread interrupted
+ * @param interruptible if should report interruption vs reset
*/
- private void doAcquireInterruptibly(int arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.EXCLUSIVE);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
- }
-
- /**
- * Acquires in exclusive timed mode.
- *
- * @param arg the acquire argument
- * @param nanosTimeout max wait time
- * @return {@code true} if acquired
- */
- private boolean doAcquireNanos(int arg, long nanosTimeout)
- throws InterruptedException {
- if (nanosTimeout <= 0L)
- return false;
- final long deadline = System.nanoTime() + nanosTimeout;
- final Node node = addWaiter(Node.EXCLUSIVE);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return true;
- }
- nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L) {
- cancelAcquire(node);
- return false;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
+ private int cancelAcquire(Node node, boolean interrupted,
+ boolean interruptible) {
+ if (node != null) {
+ node.waiter = null;
+ node.status = CANCELLED;
+ if (node.prev != null)
+ cleanQueue();
}
- }
-
- /**
- * Acquires in shared uninterruptible mode.
- * @param arg the acquire argument
- */
- private void doAcquireShared(int arg) {
- final Node node = addWaiter(Node.SHARED);
- boolean interrupted = false;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- int r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node))
- interrupted |= parkAndCheckInterrupt();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- } finally {
- if (interrupted)
- selfInterrupt();
+ if (interrupted) {
+ if (interruptible)
+ return CANCELLED;
+ else
+ Thread.currentThread().interrupt();
}
- }
-
- /**
- * Acquires in shared interruptible mode.
- * @param arg the acquire argument
- */
- private void doAcquireSharedInterruptibly(int arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.SHARED);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- int r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
- }
-
- /**
- * Acquires in shared timed mode.
- *
- * @param arg the acquire argument
- * @param nanosTimeout max wait time
- * @return {@code true} if acquired
- */
- private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
- throws InterruptedException {
- if (nanosTimeout <= 0L)
- return false;
- final long deadline = System.nanoTime() + nanosTimeout;
- final Node node = addWaiter(Node.SHARED);
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- int r = tryAcquireShared(arg);
- if (r >= 0) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- return true;
- }
- }
- nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L) {
- cancelAcquire(node);
- return false;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- throw t;
- }
+ return 0;
}
// Main exported methods
@@ -1236,9 +933,8 @@
* can represent anything you like.
*/
public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
+ if (!tryAcquire(arg))
+ acquire(null, arg, false, false, false, 0L);
}
/**
@@ -1256,11 +952,10 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(int arg)
- throws InterruptedException {
- if (Thread.interrupted())
+ throws InterruptedException {
+ if (Thread.interrupted() ||
+ (!tryAcquire(arg) && acquire(null, arg, false, true, false, 0L) < 0))
throw new InterruptedException();
- if (!tryAcquire(arg))
- doAcquireInterruptibly(arg);
}
/**
@@ -1281,11 +976,20 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
- throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquire(arg) ||
- doAcquireNanos(arg, nanosTimeout);
+ throws InterruptedException {
+ if (!Thread.interrupted()) {
+ if (tryAcquire(arg))
+ return true;
+ if (nanosTimeout <= 0L)
+ return false;
+ int stat = acquire(null, arg, false, true, true,
+ System.nanoTime() + nanosTimeout);
+ if (stat > 0)
+ return true;
+ if (stat == 0)
+ return false;
+ }
+ throw new InterruptedException();
}
/**
@@ -1300,9 +1004,7 @@
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
+ signalNext(head);
return true;
}
return false;
@@ -1321,7 +1023,7 @@
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
- doAcquireShared(arg);
+ acquire(null, arg, true, false, false, 0L);
}
/**
@@ -1338,11 +1040,11 @@
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireSharedInterruptibly(int arg)
- throws InterruptedException {
- if (Thread.interrupted())
+ throws InterruptedException {
+ if (Thread.interrupted() ||
+ (tryAcquireShared(arg) < 0 &&
+ acquire(null, arg, true, true, false, 0L) < 0))
throw new InterruptedException();
- if (tryAcquireShared(arg) < 0)
- doAcquireSharedInterruptibly(arg);
}
/**
@@ -1363,10 +1065,19 @@
*/
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquireShared(arg) >= 0 ||
- doAcquireSharedNanos(arg, nanosTimeout);
+ if (!Thread.interrupted()) {
+ if (tryAcquireShared(arg) >= 0)
+ return true;
+ if (nanosTimeout <= 0L)
+ return false;
+ int stat = acquire(null, arg, true, true, true,
+ System.nanoTime() + nanosTimeout);
+ if (stat > 0)
+ return true;
+ if (stat == 0)
+ return false;
+ }
+ throw new InterruptedException();
}
/**
@@ -1380,7 +1091,7 @@
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
- doReleaseShared();
+ signalNext(head);
return true;
}
return false;
@@ -1398,7 +1109,7 @@
*/
public final boolean hasQueuedThreads() {
for (Node p = tail, h = head; p != h && p != null; p = p.prev)
- if (p.waitStatus <= 0)
+ if (p.status >= 0)
return true;
return false;
}
@@ -1428,45 +1139,16 @@
* {@code null} if no threads are currently queued
*/
public final Thread getFirstQueuedThread() {
- // handle only fast path, else relay
- return (head == tail) ? null : fullGetFirstQueuedThread();
- }
-
- /**
- * Version of getFirstQueuedThread called when fastpath fails.
- */
- private Thread fullGetFirstQueuedThread() {
- /*
- * The first node is normally head.next. Try to get its
- * thread field, ensuring consistent reads: If thread
- * field is nulled out or s.prev is no longer head, then
- * some other thread(s) concurrently performed setHead in
- * between some of our reads. We try this twice before
- * resorting to traversal.
- */
- Node h, s;
- Thread st;
- if (((h = head) != null && (s = h.next) != null &&
- s.prev == head && (st = s.thread) != null) ||
- ((h = head) != null && (s = h.next) != null &&
- s.prev == head && (st = s.thread) != null))
- return st;
-
- /*
- * Head's next field might not have been set yet, or may have
- * been unset after setHead. So we must check to see if tail
- * is actually first node. If not, we continue on, safely
- * traversing from tail back to head to find first,
- * guaranteeing termination.
- */
-
- Thread firstThread = null;
- for (Node p = tail; p != null && p != head; p = p.prev) {
- Thread t = p.thread;
- if (t != null)
- firstThread = t;
+ Thread first = null, w; Node h, s;
+ if ((h = head) != null && ((s = h.next) == null ||
+ (first = s.waiter) == null ||
+ s.prev == null)) {
+ // traverse from tail on stale reads
+ for (Node p = tail, q; p != null && (q = p.prev) != null; p = q)
+ if ((w = p.waiter) != null)
+ first = w;
}
- return firstThread;
+ return first;
}
/**
@@ -1483,7 +1165,7 @@
if (thread == null)
throw new NullPointerException();
for (Node p = tail; p != null; p = p.prev)
- if (p.thread == thread)
+ if (p.waiter == thread)
return true;
return false;
}
@@ -1499,10 +1181,8 @@
*/
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
- return (h = head) != null &&
- (s = h.next) != null &&
- !s.isShared() &&
- s.thread != null;
+ return (h = head) != null && (s = h.next) != null &&
+ !(s instanceof SharedNode) && s.waiter != null;
}
/**
@@ -1549,19 +1229,12 @@
* @since 1.7
*/
public final boolean hasQueuedPredecessors() {
- Node h, s;
- if ((h = head) != null) {
- if ((s = h.next) == null || s.waitStatus > 0) {
- s = null; // traverse in case of concurrent cancellation
- for (Node p = tail; p != h && p != null; p = p.prev) {
- if (p.waitStatus <= 0)
- s = p;
- }
- }
- if (s != null && s.thread != Thread.currentThread())
- return true;
- }
- return false;
+ Thread first = null; Node h, s;
+ if ((h = head) != null && ((s = h.next) == null ||
+ (first = s.waiter) == null ||
+ s.prev == null))
+ first = getFirstQueuedThread(); // retry via getFirstQueuedThread
+ return first != null && first != Thread.currentThread();
}
// Instrumentation and monitoring methods
@@ -1578,7 +1251,7 @@
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
- if (p.thread != null)
+ if (p.waiter != null)
++n;
}
return n;
@@ -1598,7 +1271,7 @@
public final Collection<Thread> getQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- Thread t = p.thread;
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1616,8 +1289,8 @@
public final Collection<Thread> getExclusiveQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- if (!p.isShared()) {
- Thread t = p.thread;
+ if (!(p instanceof SharedNode)) {
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1636,8 +1309,8 @@
public final Collection<Thread> getSharedQueuedThreads() {
ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
- if (p.isShared()) {
- Thread t = p.thread;
+ if (p instanceof SharedNode) {
+ Thread t = p.waiter;
if (t != null)
list.add(t);
}
@@ -1660,117 +1333,6 @@
+ (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
-
- // Internal support methods for Conditions
-
- /**
- * Returns true if a node, always one that was initially placed on
- * a condition queue, is now waiting to reacquire on sync queue.
- * @param node the node
- * @return true if is reacquiring
- */
- final boolean isOnSyncQueue(Node node) {
- if (node.waitStatus == Node.CONDITION || node.prev == null)
- return false;
- if (node.next != null) // If has successor, it must be on queue
- return true;
- /*
- * node.prev can be non-null, but not yet on queue because
- * the CAS to place it on queue can fail. So we have to
- * traverse from tail to make sure it actually made it. It
- * will always be near the tail in calls to this method, and
- * unless the CAS failed (which is unlikely), it will be
- * there, so we hardly ever traverse much.
- */
- return findNodeFromTail(node);
- }
-
- /**
- * Returns true if node is on sync queue by searching backwards from tail.
- * Called only when needed by isOnSyncQueue.
- * @return true if present
- */
- private boolean findNodeFromTail(Node node) {
- // We check for node first, since it's likely to be at or near tail.
- // tail is known to be non-null, so we could re-order to "save"
- // one null check, but we leave it this way to help the VM.
- for (Node p = tail;;) {
- if (p == node)
- return true;
- if (p == null)
- return false;
- p = p.prev;
- }
- }
-
- /**
- * Transfers a node from a condition queue onto sync queue.
- * Returns true if successful.
- * @param node the node
- * @return true if successfully transferred (else the node was
- * cancelled before signal)
- */
- final boolean transferForSignal(Node node) {
- /*
- * If cannot change waitStatus, the node has been cancelled.
- */
- if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
- return false;
-
- /*
- * Splice onto queue and try to set waitStatus of predecessor to
- * indicate that thread is (probably) waiting. If cancelled or
- * attempt to set waitStatus fails, wake up to resync (in which
- * case the waitStatus can be transiently and harmlessly wrong).
- */
- Node p = enq(node);
- int ws = p.waitStatus;
- if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
- LockSupport.unpark(node.thread);
- return true;
- }
-
- /**
- * Transfers node, if necessary, to sync queue after a cancelled wait.
- * Returns true if thread was cancelled before being signalled.
- *
- * @param node the node
- * @return true if cancelled before the node was signalled
- */
- final boolean transferAfterCancelledWait(Node node) {
- if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
- enq(node);
- return true;
- }
- /*
- * If we lost out to a signal(), then we can't proceed
- * until it finishes its enq(). Cancelling during an
- * incomplete transfer is both rare and transient, so just
- * spin.
- */
- while (!isOnSyncQueue(node))
- Thread.yield();
- return false;
- }
-
- /**
- * Invokes release with current state value; returns saved state.
- * Cancels node and throws exception on failure.
- * @param node the condition node for this wait
- * @return previous sync state
- */
- final int fullyRelease(Node node) {
- try {
- int savedState = getState();
- if (release(savedState))
- return savedState;
- throw new IllegalMonitorStateException();
- } catch (Throwable t) {
- node.waitStatus = Node.CANCELLED;
- throw t;
- }
- }
-
// Instrumentation methods for conditions
/**
@@ -1868,106 +1430,34 @@
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
- private transient Node firstWaiter;
+ private transient ConditionNode firstWaiter;
/** Last node of condition queue. */
- private transient Node lastWaiter;
+ private transient ConditionNode lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
- // Internal methods
-
- /**
- * Adds a new waiter to wait queue.
- * @return its new wait node
- */
- private Node addConditionWaiter() {
- if (!isHeldExclusively())
- throw new IllegalMonitorStateException();
- Node t = lastWaiter;
- // If lastWaiter is cancelled, clean out.
- if (t != null && t.waitStatus != Node.CONDITION) {
- unlinkCancelledWaiters();
- t = lastWaiter;
- }
-
- Node node = new Node(Node.CONDITION);
-
- if (t == null)
- firstWaiter = node;
- else
- t.nextWaiter = node;
- lastWaiter = node;
- return node;
- }
-
- /**
- * Removes and transfers nodes until hit non-cancelled one or
- * null. Split out from signal in part to encourage compilers
- * to inline the case of no waiters.
- * @param first (non-null) the first node on condition queue
- */
- private void doSignal(Node first) {
- do {
- if ( (firstWaiter = first.nextWaiter) == null)
- lastWaiter = null;
- first.nextWaiter = null;
- } while (!transferForSignal(first) &&
- (first = firstWaiter) != null);
- }
+ // Signalling methods
/**
- * Removes and transfers all nodes.
- * @param first (non-null) the first node on condition queue
+ * Removes and transfers one or all waiters to sync queue.
*/
- private void doSignalAll(Node first) {
- lastWaiter = firstWaiter = null;
- do {
- Node next = first.nextWaiter;
- first.nextWaiter = null;
- transferForSignal(first);
+ private void doSignal(ConditionNode first, boolean all) {
+ while (first != null) {
+ ConditionNode next = first.nextWaiter;
+ if ((firstWaiter = next) == null)
+ lastWaiter = null;
+ if ((first.getAndUnsetStatus(COND) & COND) != 0) {
+ enqueue(first);
+ if (!all)
+ break;
+ }
first = next;
- } while (first != null);
- }
-
- /**
- * Unlinks cancelled waiter nodes from condition queue.
- * Called only while holding lock. This is called when
- * cancellation occurred during condition wait, and upon
- * insertion of a new waiter when lastWaiter is seen to have
- * been cancelled. This method is needed to avoid garbage
- * retention in the absence of signals. So even though it may
- * require a full traversal, it comes into play only when
- * timeouts or cancellations occur in the absence of
- * signals. It traverses all nodes rather than stopping at a
- * particular target to unlink all pointers to garbage nodes
- * without requiring many re-traversals during cancellation
- * storms.
- */
- private void unlinkCancelledWaiters() {
- Node t = firstWaiter;
- Node trail = null;
- while (t != null) {
- Node next = t.nextWaiter;
- if (t.waitStatus != Node.CONDITION) {
- t.nextWaiter = null;
- if (trail == null)
- firstWaiter = next;
- else
- trail.nextWaiter = next;
- if (next == null)
- lastWaiter = trail;
- }
- else
- trail = t;
- t = next;
}
}
- // public methods
-
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
@@ -1977,11 +1467,11 @@
* returns {@code false}
*/
public final void signal() {
+ ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- Node first = firstWaiter;
if (first != null)
- doSignal(first);
+ doSignal(first, false);
}
/**
@@ -1992,11 +1482,72 @@
* returns {@code false}
*/
public final void signalAll() {
+ ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- Node first = firstWaiter;
if (first != null)
- doSignalAll(first);
+ doSignal(first, true);
+ }
+
+ // Waiting methods
+
+ /**
+ * Adds node to condition list and releases lock.
+ *
+ * @param node the node
+ * @return savedState to reacquire after wait
+ */
+ private int enableWait(ConditionNode node) {
+ if (isHeldExclusively()) {
+ node.waiter = Thread.currentThread();
+ node.setStatusRelaxed(COND | WAITING);
+ ConditionNode last = lastWaiter;
+ if (last == null)
+ firstWaiter = node;
+ else
+ last.nextWaiter = node;
+ lastWaiter = node;
+ int savedState = getState();
+ if (release(savedState))
+ return savedState;
+ }
+ node.status = CANCELLED; // lock not held or inconsistent
+ throw new IllegalMonitorStateException();
+ }
+
+ /**
+ * Returns true if a node that was initially placed on a condition
+ * queue is now ready to reacquire on sync queue.
+ * @param node the node
+ * @return true if is reacquiring
+ */
+ private boolean canReacquire(ConditionNode node) {
+ // check links, not status to avoid enqueue race
+ return node != null && node.prev != null && isEnqueued(node);
+ }
+
+ /**
+ * Unlinks the given node and other non-waiting nodes from
+ * condition queue unless already unlinked.
+ */
+ private void unlinkCancelledWaiters(ConditionNode node) {
+ if (node == null || node.nextWaiter != null || node == lastWaiter) {
+ ConditionNode w = firstWaiter, trail = null;
+ while (w != null) {
+ ConditionNode next = w.nextWaiter;
+ if ((w.status & COND) == 0) {
+ w.nextWaiter = null;
+ if (trail == null)
+ firstWaiter = next;
+ else
+ trail.nextWaiter = next;
+ if (next == null)
+ lastWaiter = trail;
+ } else
+ trail = w;
+ w = next;
+ }
+ }
}
/**
@@ -2011,51 +1562,27 @@
* </ol>
*/
public final void awaitUninterruptibly() {
- Node node = addConditionWaiter();
- int savedState = fullyRelease(node);
+ ConditionNode node = new ConditionNode();
+ int savedState = enableWait(node);
+ LockSupport.setCurrentBlocker(this); // for back-compatibility
boolean interrupted = false;
- while (!isOnSyncQueue(node)) {
- LockSupport.park(this);
+ while (!canReacquire(node)) {
if (Thread.interrupted())
interrupted = true;
+ else if ((node.status & COND) != 0) {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ } else
+ Thread.onSpinWait(); // awoke while enqueuing
}
- if (acquireQueued(node, savedState) || interrupted)
- selfInterrupt();
- }
-
- /*
- * For interruptible waits, we need to track whether to throw
- * InterruptedException, if interrupted while blocked on
- * condition, versus reinterrupt current thread, if
- * interrupted while blocked waiting to re-acquire.
- */
-
- /** Mode meaning to reinterrupt on exit from wait */
- private static final int REINTERRUPT = 1;
- /** Mode meaning to throw InterruptedException on exit from wait */
- private static final int THROW_IE = -1;
-
- /**
- * Checks for interrupt, returning THROW_IE if interrupted
- * before signalled, REINTERRUPT if after signalled, or
- * 0 if not interrupted.
- */
- private int checkInterruptWhileWaiting(Node node) {
- return Thread.interrupted() ?
- (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
- 0;
- }
-
- /**
- * Throws InterruptedException, reinterrupts current thread, or
- * does nothing, depending on mode.
- */
- private void reportInterruptAfterWait(int interruptMode)
- throws InterruptedException {
- if (interruptMode == THROW_IE)
- throw new InterruptedException();
- else if (interruptMode == REINTERRUPT)
- selfInterrupt();
+ LockSupport.setCurrentBlocker(null);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (interrupted)
+ Thread.currentThread().interrupt();
}
/**
@@ -2074,20 +1601,33 @@
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
- Node node = addConditionWaiter();
- int savedState = fullyRelease(node);
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- LockSupport.park(this);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
+ ConditionNode node = new ConditionNode();
+ int savedState = enableWait(node);
+ LockSupport.setCurrentBlocker(this); // for back-compatibility
+ boolean interrupted = false, cancelled = false;
+ while (!canReacquire(node)) {
+ if (interrupted |= Thread.interrupted()) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break; // else interrupted after signal
+ } else if ((node.status & COND) != 0) {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ } else
+ Thread.onSpinWait(); // awoke while enqueuing
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null) // clean up if cancelled
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
+ LockSupport.setCurrentBlocker(null);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (interrupted) {
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ throw new InterruptedException();
+ }
+ Thread.currentThread().interrupt();
+ }
}
/**
@@ -2107,32 +1647,29 @@
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
- // We don't check for nanosTimeout <= 0L here, to allow
- // awaitNanos(0) as a way to "yield the lock".
- final long deadline = System.nanoTime() + nanosTimeout;
- long initialNanos = nanosTimeout;
- Node node = addConditionWaiter();
- int savedState = fullyRelease(node);
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (nanosTimeout <= 0L) {
- transferAfterCancelledWait(node);
- break;
- }
- if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
- nanosTimeout = deadline - System.nanoTime();
+ ConditionNode node = new ConditionNode();
+ int savedState = enableWait(node);
+ long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
+ long deadline = System.nanoTime() + nanos;
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ (nanos = deadline - System.nanoTime()) <= 0L) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkNanos(this, nanos);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
long remaining = deadline - System.nanoTime(); // avoid overflow
- return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
+ return (remaining <= nanosTimeout) ? remaining : Long.MIN_VALUE;
}
/**
@@ -2154,26 +1691,26 @@
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
- Node node = addConditionWaiter();
- int savedState = fullyRelease(node);
- boolean timedout = false;
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() >= abstime) {
- timedout = transferAfterCancelledWait(node);
- break;
- }
- LockSupport.parkUntil(this, abstime);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
+ ConditionNode node = new ConditionNode();
+ int savedState = enableWait(node);
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ System.currentTimeMillis() >= abstime) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkUntil(this, abstime);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
- return !timedout;
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
+ return !cancelled;
}
/**
@@ -2195,31 +1732,28 @@
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
- // We don't check for nanosTimeout <= 0L here, to allow
- // await(0, unit) as a way to "yield the lock".
- final long deadline = System.nanoTime() + nanosTimeout;
- Node node = addConditionWaiter();
- int savedState = fullyRelease(node);
- boolean timedout = false;
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) {
- if (nanosTimeout <= 0L) {
- timedout = transferAfterCancelledWait(node);
- break;
- }
- if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
- LockSupport.parkNanos(this, nanosTimeout);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break;
- nanosTimeout = deadline - System.nanoTime();
+ ConditionNode node = new ConditionNode();
+ int savedState = enableWait(node);
+ long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
+ long deadline = System.nanoTime() + nanos;
+ boolean cancelled = false, interrupted = false;
+ while (!canReacquire(node)) {
+ if ((interrupted |= Thread.interrupted()) ||
+ (nanos = deadline - System.nanoTime()) <= 0L) {
+ if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
+ break;
+ } else
+ LockSupport.parkNanos(this, nanos);
}
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters();
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
- return !timedout;
+ node.clearStatus();
+ acquire(node, savedState, false, false, false, 0L);
+ if (cancelled) {
+ unlinkCancelledWaiters(node);
+ if (interrupted)
+ throw new InterruptedException();
+ } else if (interrupted)
+ Thread.currentThread().interrupt();
+ return !cancelled;
}
// support for instrumentation
@@ -2245,8 +1779,8 @@
protected final boolean hasWaiters() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION)
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0)
return true;
}
return false;
@@ -2265,8 +1799,8 @@
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION)
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0)
++n;
}
return n;
@@ -2285,9 +1819,9 @@
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
ArrayList<Thread> list = new ArrayList<>();
- for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
- if (w.waitStatus == Node.CONDITION) {
- Thread t = w.thread;
+ for (ConditionNode w = firstWaiter; w != null; w = w.nextWaiter) {
+ if ((w.status & COND) != 0) {
+ Thread t = w.waiter;
if (t != null)
list.add(t);
}
@@ -2296,39 +1830,16 @@
}
}
- // VarHandle mechanics
- private static final VarHandle STATE;
- private static final VarHandle HEAD;
- private static final VarHandle TAIL;
+ // Unsafe
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final long STATE
+ = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "state");
+ private static final long HEAD
+ = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "head");
+ private static final long TAIL
+ = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "tail");
static {
- try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
- HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);
- TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);
- } catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
- }
-
- // Reduce the risk of rare disastrous classloading in first call to
- // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
Class<?> ensureLoaded = LockSupport.class;
}
-
- /**
- * Initializes head and tail fields on first contention.
- */
- private final void initializeSyncQueue() {
- Node h;
- if (HEAD.compareAndSet(this, null, (h = new Node())))
- tail = h;
- }
-
- /**
- * CASes tail field.
- */
- private final boolean compareAndSetTail(Node expect, Node update) {
- return TAIL.compareAndSet(this, expect, update);
- }
}
--- a/src/java.base/share/classes/java/util/concurrent/locks/Lock.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/Lock.java Wed Sep 18 07:46:02 2019 +0200
@@ -122,9 +122,8 @@
* <p>All {@code Lock} implementations <em>must</em> enforce the same
* memory synchronization semantics as provided by the built-in monitor
* lock, as described in
- * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-17.html#jls-17.4">
* Chapter 17 of
- * <cite>The Java™ Language Specification</cite></a>:
+ * <cite>The Java™ Language Specification</cite>:
* <ul>
* <li>A successful {@code lock} operation has the same memory
* synchronization effects as a successful <em>Lock</em> action.
@@ -162,6 +161,7 @@
* @see ReentrantLock
* @see Condition
* @see ReadWriteLock
+ * @jls 17.4 Memory Model
*
* @since 1.5
* @author Doug Lea
--- a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java Wed Sep 18 07:46:02 2019 +0200
@@ -140,8 +140,25 @@
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
- // Even though volatile, hotspot doesn't need a write barrier here.
- U.putReference(t, PARKBLOCKER, arg);
+ U.putReferenceOpaque(t, PARKBLOCKER, arg);
+ }
+
+ /**
+ * Sets the object to be returned by invocations of {@link
+ * #getBlocker getBlocker} for the current thread. This method may
+ * be used before invoking the no-argument version of {@link
+ * LockSupport#park() park()} from non-public objects, allowing
+ * more helpful diagnostics, or retaining compatibility with
+ * previous implementations of blocking methods. Previous values
+ * of the blocker are not automatically restored after blocking.
+ * To obtain the effects of {@code park(b}}, use {@code
+ * setCurrentBlocker(b); park(); setCurrentBlocker(null);}
+ *
+ * @param blocker the blocker object
+ * @since 14
+ */
+ public static void setCurrentBlocker(Object blocker) {
+ U.putReferenceOpaque(Thread.currentThread(), PARKBLOCKER, blocker);
}
/**
@@ -292,7 +309,7 @@
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
- return U.getReferenceVolatile(t, PARKBLOCKER);
+ return U.getReferenceOpaque(t, PARKBLOCKER);
}
/**
@@ -394,24 +411,6 @@
}
/**
- * Returns the pseudo-randomly initialized or updated secondary seed.
- * Copied from ThreadLocalRandom due to package access restrictions.
- */
- static final int nextSecondarySeed() {
- int r;
- Thread t = Thread.currentThread();
- if ((r = U.getInt(t, SECONDARY)) != 0) {
- r ^= r << 13; // xorshift
- r ^= r >>> 17;
- r ^= r << 5;
- }
- else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
- r = 1; // avoid zero
- U.putInt(t, SECONDARY, r);
- return r;
- }
-
- /**
* Returns the thread id for the given thread. We must access
* this directly rather than via method Thread.getId() because
* getId() has been known to be overridden in ways that do not
@@ -423,11 +422,9 @@
// Hotspot implementation via intrinsics API
private static final Unsafe U = Unsafe.getUnsafe();
- private static final long PARKBLOCKER = U.objectFieldOffset
- (Thread.class, "parkBlocker");
- private static final long SECONDARY = U.objectFieldOffset
- (Thread.class, "threadLocalRandomSecondarySeed");
- private static final long TID = U.objectFieldOffset
- (Thread.class, "tid");
+ private static final long PARKBLOCKER
+ = U.objectFieldOffset(Thread.class, "parkBlocker");
+ private static final long TID
+ = U.objectFieldOffset(Thread.class, "tid");
}
--- a/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Wed Sep 18 07:46:02 2019 +0200
@@ -119,39 +119,63 @@
private static final long serialVersionUID = -5179523762034025860L;
/**
- * Performs non-fair tryLock. tryAcquire is implemented in
- * subclasses, but both need nonfair try for trylock method.
+ * Performs non-fair tryLock.
*/
@ReservedStackAccess
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
+ final boolean tryLock() {
+ Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
- if (compareAndSetState(0, acquires)) {
+ if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0) // overflow
+ } else if (getExclusiveOwnerThread() == current) {
+ if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
- setState(nextc);
+ setState(c);
return true;
}
return false;
}
+ /**
+ * Checks for reentrancy and acquires if lock immediately
+ * available under fair vs nonfair rules. Locking methods
+ * perform initialTryLock check before relaying to
+ * corresponding AQS acquire methods.
+ */
+ abstract boolean initialTryLock();
+
+ @ReservedStackAccess
+ final void lock() {
+ if (!initialTryLock())
+ acquire(1);
+ }
+
+ @ReservedStackAccess
+ final void lockInterruptibly() throws InterruptedException {
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ if (!initialTryLock())
+ acquireInterruptibly(1);
+ }
+
+ @ReservedStackAccess
+ final boolean tryLockNanos(long nanos) throws InterruptedException {
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ return initialTryLock() || tryAcquireNanos(1, nanos);
+ }
+
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
- if (Thread.currentThread() != getExclusiveOwnerThread())
+ if (getExclusiveOwnerThread() != Thread.currentThread())
throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- free = true;
+ boolean free = (c == 0);
+ if (free)
setExclusiveOwnerThread(null);
- }
setState(c);
return free;
}
@@ -195,8 +219,31 @@
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
+
+ final boolean initialTryLock() {
+ Thread current = Thread.currentThread();
+ if (compareAndSetState(0, 1)) { // first attempt is unguarded
+ setExclusiveOwnerThread(current);
+ return true;
+ } else if (getExclusiveOwnerThread() == current) {
+ int c = getState() + 1;
+ if (c < 0) // overflow
+ throw new Error("Maximum lock count exceeded");
+ setState(c);
+ return true;
+ } else
+ return false;
+ }
+
+ /**
+ * Acquire for non-reentrant cases after initialTryLock prescreen
+ */
protected final boolean tryAcquire(int acquires) {
- return nonfairTryAcquire(acquires);
+ if (getState() == 0 && compareAndSetState(0, acquires)) {
+ setExclusiveOwnerThread(Thread.currentThread());
+ return true;
+ }
+ return false;
}
}
@@ -205,26 +252,34 @@
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
+
/**
- * Fair version of tryAcquire. Don't grant access unless
- * recursive call or no waiters or is first.
+ * Acquires only if reentrant or queue is empty.
*/
- @ReservedStackAccess
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
+ final boolean initialTryLock() {
+ Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {
+ if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
+ } else if (getExclusiveOwnerThread() == current) {
+ if (++c < 0) // overflow
+ throw new Error("Maximum lock count exceeded");
+ setState(c);
+ return true;
}
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
+ return false;
+ }
+
+ /**
+ * Acquires only if thread is first waiter or empty
+ */
+ protected final boolean tryAcquire(int acquires) {
+ if (getState() == 0 && !hasQueuedPredecessors() &&
+ compareAndSetState(0, acquires)) {
+ setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
@@ -264,7 +319,7 @@
* at which time the lock hold count is set to one.
*/
public void lock() {
- sync.acquire(1);
+ sync.lock();
}
/**
@@ -314,7 +369,7 @@
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
- sync.acquireInterruptibly(1);
+ sync.lockInterruptibly();
}
/**
@@ -344,7 +399,7 @@
* thread; and {@code false} otherwise
*/
public boolean tryLock() {
- return sync.nonfairTryAcquire(1);
+ return sync.tryLock();
}
/**
@@ -421,7 +476,7 @@
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
- return sync.tryAcquireNanos(1, unit.toNanos(timeout));
+ return sync.tryLockNanos(unit.toNanos(timeout));
}
/**
--- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Wed Sep 18 07:46:02 2019 +0200
@@ -35,9 +35,8 @@
package java.util.concurrent.locks;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.concurrent.TimeUnit;
+import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ReservedStackAccess;
/**
@@ -132,9 +131,8 @@
*
* <p><b>Memory Synchronization.</b> Methods with the effect of
* successfully locking in any mode have the same memory
- * synchronization effects as a <em>Lock</em> action described in
- * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-17.html#jls-17.4">
- * Chapter 17 of <cite>The Java™ Language Specification</cite></a>.
+ * synchronization effects as a <em>Lock</em> action, as described in
+ * Chapter 17 of <cite>The Java™ Language Specification</cite>.
* Methods successfully unlocking in write mode have the same memory
* synchronization effects as an <em>Unlock</em> action. In optimistic
* read usages, actions prior to the most recent write mode unlock action
@@ -237,6 +235,7 @@
* }
* }}</pre>
*
+ * @jls 17.4 Memory Model
* @since 1.8
* @author Doug Lea
*/
@@ -264,122 +263,54 @@
* updates.
*
* Waiters use a modified form of CLH lock used in
- * AbstractQueuedSynchronizer (see its internal documentation for
- * a fuller account), where each node is tagged (field mode) as
- * either a reader or writer. Sets of waiting readers are grouped
- * (linked) under a common node (field cowait) so act as a single
- * node with respect to most CLH mechanics. By virtue of the
- * queue structure, wait nodes need not actually carry sequence
- * numbers; we know each is greater than its predecessor. This
- * simplifies the scheduling policy to a mainly-FIFO scheme that
- * incorporates elements of Phase-Fair locks (see Brandenburg &
- * Anderson, especially http://www.cs.unc.edu/~bbb/diss/). In
- * particular, we use the phase-fair anti-barging rule: If an
- * incoming reader arrives while read lock is held but there is a
- * queued writer, this incoming reader is queued. (This rule is
- * responsible for some of the complexity of method acquireRead,
- * but without it, the lock becomes highly unfair.) Method release
- * does not (and sometimes cannot) itself wake up cowaiters. This
- * is done by the primary thread, but helped by any other threads
- * with nothing better to do in methods acquireRead and
- * acquireWrite.
+ * AbstractQueuedSynchronizer (AQS; see its internal documentation
+ * for a fuller account), where each node is either a ReaderNode
+ * or WriterNode. Implementation of queued Writer mode is
+ * identical to AQS except for lock-state operations. Sets of
+ * waiting readers are grouped (linked) under a common node (field
+ * cowaiters) so act as a single node with respect to most CLH
+ * mechanics. This simplifies the scheduling policy to a
+ * mainly-FIFO scheme that incorporates elements of Phase-Fair
+ * locks (see Brandenburg & Anderson, especially
+ * http://www.cs.unc.edu/~bbb/diss/). Method release does not
+ * itself wake up cowaiters. This is done by the primary thread,
+ * but helped by other cowaiters as they awaken.
*
- * These rules apply to threads actually queued. All tryLock forms
- * opportunistically try to acquire locks regardless of preference
- * rules, and so may "barge" their way in. Randomized spinning is
- * used in the acquire methods to reduce (increasingly expensive)
- * context switching while also avoiding sustained memory
- * thrashing among many threads. We limit spins to the head of
- * queue. If, upon wakening, a thread fails to obtain lock, and is
- * still (or becomes) the first waiting thread (which indicates
- * that some other thread barged and obtained lock), it escalates
- * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
- * continually losing to barging threads.
+ * These rules apply to threads actually queued. Threads may also
+ * try to acquire locks before or in the process of enqueueing
+ * regardless of preference rules, and so may "barge" their way
+ * in. Methods writeLock and readLock (but not the other variants
+ * of each) first unconditionally try to CAS state, falling back
+ * to test-and-test-and-set retries on failure, slightly shrinking
+ * race windows on initial attempts, thus making success more
+ * likely. Also, when some threads cancel (via interrupt or
+ * timeout), phase-fairness is at best roughly approximated.
*
* Nearly all of these mechanics are carried out in methods
* acquireWrite and acquireRead, that, as typical of such code,
* sprawl out because actions and retries rely on consistent sets
* of locally cached reads.
*
- * As noted in Boehm's paper (above), sequence validation (mainly
- * method validate()) requires stricter ordering rules than apply
- * to normal volatile reads (of "state"). To force orderings of
- * reads before a validation and the validation itself in those
- * cases where this is not already forced, we use acquireFence.
- * Unlike in that paper, we allow writers to use plain writes.
- * One would not expect reorderings of such writes with the lock
- * acquisition CAS because there is a "control dependency", but it
- * is theoretically possible, so we additionally add a
- * storeStoreFence after lock acquisition CAS.
- *
- * ----------------------------------------------------------------
- * Here's an informal proof that plain reads by _successful_
- * readers see plain writes from preceding but not following
- * writers (following Boehm and the C++ standard [atomics.fences]):
- *
- * Because of the total synchronization order of accesses to
- * volatile long state containing the sequence number, writers and
- * _successful_ readers can be globally sequenced.
- *
- * int x, y;
- *
- * Writer 1:
- * inc sequence (odd - "locked")
- * storeStoreFence();
- * x = 1; y = 2;
- * inc sequence (even - "unlocked")
- *
- * Successful Reader:
- * read sequence (even)
- * // must see writes from Writer 1 but not Writer 2
- * r1 = x; r2 = y;
- * acquireFence();
- * read sequence (even - validated unchanged)
- * // use r1 and r2
- *
- * Writer 2:
- * inc sequence (odd - "locked")
- * storeStoreFence();
- * x = 3; y = 4;
- * inc sequence (even - "unlocked")
- *
- * Visibility of writer 1's stores is normal - reader's initial
- * read of state synchronizes with writer 1's final write to state.
- * Lack of visibility of writer 2's plain writes is less obvious.
- * If reader's read of x or y saw writer 2's write, then (assuming
- * semantics of C++ fences) the storeStoreFence would "synchronize"
- * with reader's acquireFence and reader's validation read must see
- * writer 2's initial write to state and so validation must fail.
- * But making this "proof" formal and rigorous is an open problem!
- * ----------------------------------------------------------------
+ * For an explanation of the use of acquireFence, see
+ * http://gee.cs.oswego.edu/dl/html/j9mm.html as well as Boehm's
+ * paper (above). Note that sequence validation (mainly method
+ * validate()) requires stricter ordering rules than apply to
+ * normal volatile reads (of "state"). To ensure that writeLock
+ * acquisitions strictly precede subsequent writes in cases where
+ * this is not already forced, we use a storeStoreFence.
*
* The memory layout keeps lock state and queue pointers together
* (normally on the same cache line). This usually works well for
* read-mostly loads. In most other cases, the natural tendency of
- * adaptive-spin CLH locks to reduce memory contention lessens
- * motivation to further spread out contended locations, but might
- * be subject to future improvements.
+ * CLH locks to reduce memory contention lessens motivation to
+ * further spread out contended locations, but might be subject to
+ * future improvements.
*/
private static final long serialVersionUID = -6001602636862214147L;
- /** Number of processors, for spin control */
- private static final int NCPU = Runtime.getRuntime().availableProcessors();
-
- /** Maximum number of retries before enqueuing on acquisition; at least 1 */
- private static final int SPINS = (NCPU > 1) ? 1 << 6 : 1;
-
- /** Maximum number of tries before blocking at head on acquisition */
- private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 1;
-
- /** Maximum number of retries before re-blocking */
- private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 1;
-
- /** The period for yielding when waiting for overflow spinlock */
- private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
-
/** The number of bits to use for reader count before overflowing */
- private static final int LG_READERS = 7;
+ private static final int LG_READERS = 7; // 127 readers
// Values for lock state and stamp operations
private static final long RUNIT = 1L;
@@ -388,6 +319,8 @@
private static final long RFULL = RBITS - 1L;
private static final long ABITS = RBITS | WBIT;
private static final long SBITS = ~RBITS; // note overlap with ABITS
+ // not writing and conservatively non-overflowing
+ private static final long RSAFE = ~(3L << (LG_READERS - 1));
/*
* 3 stamp modes can be distinguished by examining (m = stamp & ABITS):
@@ -408,29 +341,64 @@
// Special value from cancelled acquire methods so caller can throw IE
private static final long INTERRUPTED = 1L;
- // Values for node status; order matters
- private static final int WAITING = -1;
- private static final int CANCELLED = 1;
+ // Bits for Node.status
+ static final int WAITING = 1;
+ static final int CANCELLED = 0x80000000; // must be negative
- // Modes for nodes (int not boolean to allow arithmetic)
- private static final int RMODE = 0;
- private static final int WMODE = 1;
+ /** CLH nodes */
+ abstract static class Node {
+ volatile Node prev; // initially attached via casTail
+ volatile Node next; // visibly nonnull when signallable
+ Thread waiter; // visibly nonnull when enqueued
+ volatile int status; // written by owner, atomic bit ops by others
- /** Wait nodes */
- static final class WNode {
- volatile WNode prev;
- volatile WNode next;
- volatile WNode cowait; // list of linked readers
- volatile Thread thread; // non-null while possibly parked
- volatile int status; // 0, WAITING, or CANCELLED
- final int mode; // RMODE or WMODE
- WNode(int m, WNode p) { mode = m; prev = p; }
+ // methods for atomic operations
+ final boolean casPrev(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, PREV, c, v);
+ }
+ final boolean casNext(Node c, Node v) { // for cleanQueue
+ return U.weakCompareAndSetReference(this, NEXT, c, v);
+ }
+ final int getAndUnsetStatus(int v) { // for signalling
+ return U.getAndBitwiseAndInt(this, STATUS, ~v);
+ }
+ final void setPrevRelaxed(Node p) { // for off-queue assignment
+ U.putReference(this, PREV, p);
+ }
+ final void setStatusRelaxed(int s) { // for off-queue assignment
+ U.putInt(this, STATUS, s);
+ }
+ final void clearStatus() { // for reducing unneeded signals
+ U.putIntOpaque(this, STATUS, 0);
+ }
+
+ private static final long STATUS
+ = U.objectFieldOffset(Node.class, "status");
+ private static final long NEXT
+ = U.objectFieldOffset(Node.class, "next");
+ private static final long PREV
+ = U.objectFieldOffset(Node.class, "prev");
+ }
+
+ static final class WriterNode extends Node { // node for writers
+ }
+
+ static final class ReaderNode extends Node { // node for readers
+ volatile ReaderNode cowaiters; // list of linked readers
+ final boolean casCowaiters(ReaderNode c, ReaderNode v) {
+ return U.weakCompareAndSetReference(this, COWAITERS, c, v);
+ }
+ final void setCowaitersRelaxed(ReaderNode p) {
+ U.putReference(this, COWAITERS, p);
+ }
+ private static final long COWAITERS
+ = U.objectFieldOffset(ReaderNode.class, "cowaiters");
}
/** Head of CLH queue */
- private transient volatile WNode whead;
+ private transient volatile Node head;
/** Tail (last) of CLH queue */
- private transient volatile WNode wtail;
+ private transient volatile Node tail;
// views
transient ReadLockView readLockView;
@@ -449,18 +417,50 @@
state = ORIGIN;
}
- private boolean casState(long expectedValue, long newValue) {
- return STATE.compareAndSet(this, expectedValue, newValue);
+ // internal lock methods
+
+ private boolean casState(long expect, long update) {
+ return U.compareAndSetLong(this, STATE, expect, update);
+ }
+
+ @ReservedStackAccess
+ private long tryAcquireWrite() {
+ long s, nextState;
+ if (((s = state) & ABITS) == 0L && casState(s, nextState = s | WBIT)) {
+ U.storeStoreFence();
+ return nextState;
+ }
+ return 0L;
}
- private long tryWriteLock(long s) {
- // assert (s & ABITS) == 0L;
- long next;
- if (casState(s, next = s | WBIT)) {
- VarHandle.storeStoreFence();
- return next;
+ @ReservedStackAccess
+ private long tryAcquireRead() {
+ for (long s, m, nextState;;) {
+ if ((m = (s = state) & ABITS) < RFULL) {
+ if (casState(s, nextState = s + RUNIT))
+ return nextState;
+ }
+ else if (m == WBIT)
+ return 0L;
+ else if ((nextState = tryIncReaderOverflow(s)) != 0L)
+ return nextState;
}
- return 0L;
+ }
+
+ /**
+ * Returns an unlocked state, incrementing the version and
+ * avoiding special failure value 0L.
+ *
+ * @param s a write-locked state (or stamp)
+ */
+ private static long unlockWriteState(long s) {
+ return ((s += WBIT) == 0L) ? ORIGIN : s;
+ }
+
+ private long releaseWrite(long s) {
+ long nextState = state = unlockWriteState(s);
+ signalNext(head);
+ return nextState;
}
/**
@@ -471,8 +471,13 @@
*/
@ReservedStackAccess
public long writeLock() {
- long next;
- return ((next = tryWriteLock()) != 0L) ? next : acquireWrite(false, 0L);
+ // try unconditional CAS confirming weak read
+ long s = U.getLongOpaque(this, STATE) & ~ABITS, nextState;
+ if (casState(s, nextState = s | WBIT)) {
+ U.storeStoreFence();
+ return nextState;
+ }
+ return acquireWrite(false, false, 0L);
}
/**
@@ -481,10 +486,8 @@
* @return a write stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
*/
- @ReservedStackAccess
public long tryWriteLock() {
- long s;
- return (((s = state) & ABITS) == 0L) ? tryWriteLock(s) : 0L;
+ return tryAcquireWrite();
}
/**
@@ -504,15 +507,14 @@
throws InterruptedException {
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
- long next, deadline;
- if ((next = tryWriteLock()) != 0L)
- return next;
+ long nextState;
+ if ((nextState = tryAcquireWrite()) != 0L)
+ return nextState;
if (nanos <= 0L)
return 0L;
- if ((deadline = System.nanoTime() + nanos) == 0L)
- deadline = 1L;
- if ((next = acquireWrite(true, deadline)) != INTERRUPTED)
- return next;
+ nextState = acquireWrite(true, true, System.nanoTime() + nanos);
+ if (nextState != INTERRUPTED)
+ return nextState;
}
throw new InterruptedException();
}
@@ -527,12 +529,12 @@
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
- @ReservedStackAccess
public long writeLockInterruptibly() throws InterruptedException {
- long next;
+ long nextState;
if (!Thread.interrupted() &&
- (next = acquireWrite(true, 0L)) != INTERRUPTED)
- return next;
+ ((nextState = tryAcquireWrite()) != 0L ||
+ (nextState = acquireWrite(true, false, 0L)) != INTERRUPTED))
+ return nextState;
throw new InterruptedException();
}
@@ -544,13 +546,12 @@
*/
@ReservedStackAccess
public long readLock() {
- long s, next;
- // bypass acquireRead on common uncontended case
- return (whead == wtail
- && ((s = state) & ABITS) < RFULL
- && casState(s, next = s + RUNIT))
- ? next
- : acquireRead(false, 0L);
+ // unconditionally optimistically try non-overflow case once
+ long s = U.getLongOpaque(this, STATE) & RSAFE, nextState;
+ if (casState(s, nextState = s + RUNIT))
+ return nextState;
+ else
+ return acquireRead(false, false, 0L);
}
/**
@@ -559,18 +560,8 @@
* @return a read stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
*/
- @ReservedStackAccess
public long tryReadLock() {
- long s, m, next;
- while ((m = (s = state) & ABITS) != WBIT) {
- if (m < RFULL) {
- if (casState(s, next = s + RUNIT))
- return next;
- }
- else if ((next = tryIncReaderOverflow(s)) != 0L)
- return next;
- }
- return 0L;
+ return tryAcquireRead();
}
/**
@@ -586,26 +577,18 @@
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
- @ReservedStackAccess
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
- long s, m, next, deadline;
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
- if ((m = (s = state) & ABITS) != WBIT) {
- if (m < RFULL) {
- if (casState(s, next = s + RUNIT))
- return next;
- }
- else if ((next = tryIncReaderOverflow(s)) != 0L)
- return next;
- }
+ long nextState;
+ if (tail == head && (nextState = tryAcquireRead()) != 0L)
+ return nextState;
if (nanos <= 0L)
return 0L;
- if ((deadline = System.nanoTime() + nanos) == 0L)
- deadline = 1L;
- if ((next = acquireRead(true, deadline)) != INTERRUPTED)
- return next;
+ nextState = acquireRead(true, true, System.nanoTime() + nanos);
+ if (nextState != INTERRUPTED)
+ return nextState;
}
throw new InterruptedException();
}
@@ -620,17 +603,12 @@
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
- @ReservedStackAccess
public long readLockInterruptibly() throws InterruptedException {
- long s, next;
- if (!Thread.interrupted()
- // bypass acquireRead on common uncontended case
- && ((whead == wtail
- && ((s = state) & ABITS) < RFULL
- && casState(s, next = s + RUNIT))
- ||
- (next = acquireRead(true, 0L)) != INTERRUPTED))
- return next;
+ long nextState;
+ if (!Thread.interrupted() &&
+ ((nextState = tryAcquireRead()) != 0L ||
+ (nextState = acquireRead(true, false, 0L)) != INTERRUPTED))
+ return nextState;
throw new InterruptedException();
}
@@ -658,29 +636,11 @@
* since issuance of the given stamp; else false
*/
public boolean validate(long stamp) {
- VarHandle.acquireFence();
+ U.loadFence();
return (stamp & SBITS) == (state & SBITS);
}
/**
- * Returns an unlocked state, incrementing the version and
- * avoiding special failure value 0L.
- *
- * @param s a write-locked state (or stamp)
- */
- private static long unlockWriteState(long s) {
- return ((s += WBIT) == 0L) ? ORIGIN : s;
- }
-
- private long unlockWriteInternal(long s) {
- long next; WNode h;
- STATE.setVolatile(this, next = unlockWriteState(s));
- if ((h = whead) != null && h.status != 0)
- release(h);
- return next;
- }
-
- /**
* If the lock state matches the given stamp, releases the
* exclusive lock.
*
@@ -692,7 +652,7 @@
public void unlockWrite(long stamp) {
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
- unlockWriteInternal(stamp);
+ releaseWrite(stamp);
}
/**
@@ -705,19 +665,20 @@
*/
@ReservedStackAccess
public void unlockRead(long stamp) {
- long s, m; WNode h;
- while (((s = state) & SBITS) == (stamp & SBITS)
- && (stamp & RBITS) > 0L
- && ((m = s & RBITS) > 0L)) {
- if (m < RFULL) {
- if (casState(s, s - RUNIT)) {
- if (m == RUNIT && (h = whead) != null && h.status != 0)
- release(h);
+ long s, m;
+ if ((stamp & RBITS) != 0L) {
+ while (((s = state) & SBITS) == (stamp & SBITS) &&
+ ((m = s & RBITS) != 0L)) {
+ if (m < RFULL) {
+ if (casState(s, s - RUNIT)) {
+ if (m == RUNIT)
+ signalNext(head);
+ return;
+ }
+ }
+ else if (tryDecReaderOverflow(s) != 0L)
return;
- }
}
- else if (tryDecReaderOverflow(s) != 0L)
- return;
}
throw new IllegalMonitorStateException();
}
@@ -730,7 +691,6 @@
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
- @ReservedStackAccess
public void unlock(long stamp) {
if ((stamp & WBIT) != 0L)
unlockWrite(stamp);
@@ -751,26 +711,23 @@
* @return a valid write stamp, or zero on failure
*/
public long tryConvertToWriteLock(long stamp) {
- long a = stamp & ABITS, m, s, next;
+ long a = stamp & ABITS, m, s, nextState;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
- if ((next = tryWriteLock(s)) != 0L)
- return next;
- }
- else if (m == WBIT) {
+ if (casState(s, nextState = s | WBIT)) {
+ U.storeStoreFence();
+ return nextState;
+ }
+ } else if (m == WBIT) {
if (a != m)
break;
return stamp;
- }
- else if (m == RUNIT && a != 0L) {
- if (casState(s, next = s - RUNIT + WBIT)) {
- VarHandle.storeStoreFence();
- return next;
- }
- }
- else
+ } else if (m == RUNIT && a != 0L) {
+ if (casState(s, nextState = s - RUNIT + WBIT))
+ return nextState;
+ } else
break;
}
return 0L;
@@ -788,28 +745,21 @@
* @return a valid read stamp, or zero on failure
*/
public long tryConvertToReadLock(long stamp) {
- long a, s, next; WNode h;
+ long a, s, nextState;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((a = stamp & ABITS) >= WBIT) {
- // write stamp
- if (s != stamp)
+ if (s != stamp) // write stamp
break;
- STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT);
- if ((h = whead) != null && h.status != 0)
- release(h);
- return next;
- }
- else if (a == 0L) {
- // optimistic read stamp
+ nextState = state = unlockWriteState(s) + RUNIT;
+ signalNext(head);
+ return nextState;
+ } else if (a == 0L) { // optimistic read stamp
if ((s & ABITS) < RFULL) {
- if (casState(s, next = s + RUNIT))
- return next;
- }
- else if ((next = tryIncReaderOverflow(s)) != 0L)
- return next;
- }
- else {
- // already a read stamp
+ if (casState(s, nextState = s + RUNIT))
+ return nextState;
+ } else if ((nextState = tryIncReaderOverflow(s)) != 0L)
+ return nextState;
+ } else { // already a read stamp
if ((s & ABITS) == 0L)
break;
return stamp;
@@ -829,29 +779,25 @@
* @return a valid optimistic read stamp, or zero on failure
*/
public long tryConvertToOptimisticRead(long stamp) {
- long a, m, s, next; WNode h;
- VarHandle.acquireFence();
+ long a, m, s, nextState;
+ U.loadFence();
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((a = stamp & ABITS) >= WBIT) {
- // write stamp
- if (s != stamp)
+ if (s != stamp) // write stamp
break;
- return unlockWriteInternal(s);
- }
- else if (a == 0L)
- // already an optimistic read stamp
+ return releaseWrite(s);
+ } else if (a == 0L) { // already an optimistic read stamp
return stamp;
- else if ((m = s & ABITS) == 0L) // invalid read stamp
+ } else if ((m = s & ABITS) == 0L) { // invalid read stamp
break;
- else if (m < RFULL) {
- if (casState(s, next = s - RUNIT)) {
- if (m == RUNIT && (h = whead) != null && h.status != 0)
- release(h);
- return next & SBITS;
+ } else if (m < RFULL) {
+ if (casState(s, nextState = s - RUNIT)) {
+ if (m == RUNIT)
+ signalNext(head);
+ return nextState & SBITS;
}
- }
- else if ((next = tryDecReaderOverflow(s)) != 0L)
- return next & SBITS;
+ } else if ((nextState = tryDecReaderOverflow(s)) != 0L)
+ return nextState & SBITS;
}
return 0L;
}
@@ -867,7 +813,7 @@
public boolean tryUnlockWrite() {
long s;
if (((s = state) & WBIT) != 0L) {
- unlockWriteInternal(s);
+ releaseWrite(s);
return true;
}
return false;
@@ -882,12 +828,12 @@
*/
@ReservedStackAccess
public boolean tryUnlockRead() {
- long s, m; WNode h;
+ long s, m;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
if (casState(s, s - RUNIT)) {
- if (m == RUNIT && (h = whead) != null && h.status != 0)
- release(h);
+ if (m == RUNIT)
+ signalNext(head);
return true;
}
}
@@ -1133,16 +1079,16 @@
long s;
if (((s = state) & WBIT) == 0L)
throw new IllegalMonitorStateException();
- unlockWriteInternal(s);
+ releaseWrite(s);
}
final void unstampedUnlockRead() {
- long s, m; WNode h;
+ long s, m;
while ((m = (s = state) & RBITS) > 0L) {
if (m < RFULL) {
if (casState(s, s - RUNIT)) {
- if (m == RUNIT && (h = whead) != null && h.status != 0)
- release(h);
+ if (m == RUNIT)
+ signalNext(head);
return;
}
}
@@ -1155,10 +1101,10 @@
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
- STATE.setVolatile(this, ORIGIN); // reset to unlocked state
+ state = ORIGIN; // reset to unlocked state
}
- // internals
+ // overflow handling methods
/**
* Tries to increment readerOverflow by first setting state
@@ -1170,17 +1116,12 @@
*/
private long tryIncReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
- if ((s & ABITS) == RFULL) {
- if (casState(s, s | RBITS)) {
- ++readerOverflow;
- STATE.setVolatile(this, s);
- return s;
- }
+ if ((s & ABITS) != RFULL)
+ Thread.onSpinWait();
+ else if (casState(s, s | RBITS)) {
+ ++readerOverflow;
+ return state = s;
}
- else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
- Thread.yield();
- else
- Thread.onSpinWait();
return 0L;
}
@@ -1192,153 +1133,132 @@
*/
private long tryDecReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
- if ((s & ABITS) == RFULL) {
- if (casState(s, s | RBITS)) {
- int r; long next;
- if ((r = readerOverflow) > 0) {
- readerOverflow = r - 1;
- next = s;
- }
- else
- next = s - RUNIT;
- STATE.setVolatile(this, next);
- return next;
+ if ((s & ABITS) != RFULL)
+ Thread.onSpinWait();
+ else if (casState(s, s | RBITS)) {
+ int r; long nextState;
+ if ((r = readerOverflow) > 0) {
+ readerOverflow = r - 1;
+ nextState = s;
}
+ else
+ nextState = s - RUNIT;
+ return state = nextState;
}
- else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
- Thread.yield();
- else
- Thread.onSpinWait();
return 0L;
}
+ // release methods
+
/**
- * Wakes up the successor of h (normally whead). This is normally
- * just h.next, but may require traversal from wtail if next
- * pointers are lagging. This may fail to wake up an acquiring
- * thread when one or more have been cancelled, but the cancel
- * methods themselves provide extra safeguards to ensure liveness.
+ * Wakes up the successor of given node, if one exists, and unsets its
+ * WAITING status to avoid park race. This may fail to wake up an
+ * eligible thread when one or more have been cancelled, but
+ * cancelAcquire ensures liveness.
*/
- private void release(WNode h) {
- if (h != null) {
- WNode q; Thread w;
- WSTATUS.compareAndSet(h, WAITING, 0);
- if ((q = h.next) == null || q.status == CANCELLED) {
- for (WNode t = wtail; t != null && t != h; t = t.prev)
- if (t.status <= 0)
- q = t;
- }
- if (q != null && (w = q.thread) != null)
- LockSupport.unpark(w);
+ static final void signalNext(Node h) {
+ Node s;
+ if (h != null && (s = h.next) != null && s.status > 0) {
+ s.getAndUnsetStatus(WAITING);
+ LockSupport.unpark(s.waiter);
}
}
/**
- * See above for explanation.
+ * Removes and unparks all cowaiters of node, if it exists.
+ */
+ private static void signalCowaiters(ReaderNode node) {
+ if (node != null) {
+ for (ReaderNode c; (c = node.cowaiters) != null; ) {
+ if (node.casCowaiters(c, c.cowaiters))
+ LockSupport.unpark(c.waiter);
+ }
+ }
+ }
+
+ // queue link methods
+ private boolean casTail(Node c, Node v) {
+ return U.compareAndSetReference(this, TAIL, c, v);
+ }
+
+ /** tries once to CAS a new dummy node for head */
+ private void tryInitializeHead() {
+ Node h = new WriterNode();
+ if (U.compareAndSetReference(this, HEAD, null, h))
+ tail = h;
+ }
+
+ /**
+ * For explanation, see above and AbstractQueuedSynchronizer
+ * internal documentation.
*
* @param interruptible true if should check interrupts and if so
* return INTERRUPTED
- * @param deadline if nonzero, the System.nanoTime value to timeout
- * at (and return zero)
+ * @param timed if true use timed waits
+ * @param time the System.nanoTime value to timeout at (and return zero)
* @return next state, or INTERRUPTED
*/
- private long acquireWrite(boolean interruptible, long deadline) {
- WNode node = null, p;
- for (int spins = -1;;) { // spin while enqueuing
- long m, s, ns;
- if ((m = (s = state) & ABITS) == 0L) {
- if ((ns = tryWriteLock(s)) != 0L)
- return ns;
+ private long acquireWrite(boolean interruptible, boolean timed, long time) {
+ byte spins = 0, postSpins = 0; // retries upon unpark of first thread
+ boolean interrupted = false, first = false;
+ WriterNode node = null;
+ Node pred = null;
+ for (long s, nextState;;) {
+ if (!first && (pred = (node == null) ? null : node.prev) != null &&
+ !(first = (head == pred))) {
+ if (pred.status < 0) {
+ cleanQueue(); // predecessor cancelled
+ continue;
+ } else if (pred.prev == null) {
+ Thread.onSpinWait(); // ensure serialization
+ continue;
+ }
}
- else if (spins < 0)
- spins = (m == WBIT && wtail == whead) ? SPINS : 0;
- else if (spins > 0) {
+ if ((first || pred == null) && ((s = state) & ABITS) == 0L &&
+ casState(s, nextState = s | WBIT)) {
+ U.storeStoreFence();
+ if (first) {
+ node.prev = null;
+ head = node;
+ pred.next = null;
+ node.waiter = null;
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+ return nextState;
+ } else if (node == null) { // retry before enqueuing
+ node = new WriterNode();
+ } else if (pred == null) { // try to enqueue
+ Node t = tail;
+ node.setPrevRelaxed(t);
+ if (t == null)
+ tryInitializeHead();
+ else if (!casTail(t, node))
+ node.setPrevRelaxed(null); // back out
+ else
+ t.next = node;
+ } else if (first && spins != 0) { // reduce unfairness
--spins;
Thread.onSpinWait();
- }
- else if ((p = wtail) == null) { // initialize queue
- WNode hd = new WNode(WMODE, null);
- if (WHEAD.weakCompareAndSet(this, null, hd))
- wtail = hd;
- }
- else if (node == null)
- node = new WNode(WMODE, p);
- else if (node.prev != p)
- node.prev = p;
- else if (WTAIL.weakCompareAndSet(this, p, node)) {
- p.next = node;
- break;
+ } else if (node.status == 0) { // enable signal
+ if (node.waiter == null)
+ node.waiter = Thread.currentThread();
+ node.status = WAITING;
+ } else {
+ long nanos;
+ spins = postSpins = (byte)((postSpins << 1) | 1);
+ if (!timed)
+ LockSupport.park(this);
+ else if ((nanos = time - System.nanoTime()) > 0L)
+ LockSupport.parkNanos(this, nanos);
+ else
+ break;
+ node.clearStatus();
+ if ((interrupted |= Thread.interrupted()) && interruptible)
+ break;
}
}
-
- boolean wasInterrupted = false;
- for (int spins = -1;;) {
- WNode h, np, pp; int ps;
- if ((h = whead) == p) {
- if (spins < 0)
- spins = HEAD_SPINS;
- else if (spins < MAX_HEAD_SPINS)
- spins <<= 1;
- for (int k = spins; k > 0; --k) { // spin at head
- long s, ns;
- if (((s = state) & ABITS) == 0L) {
- if ((ns = tryWriteLock(s)) != 0L) {
- whead = node;
- node.prev = null;
- if (wasInterrupted)
- Thread.currentThread().interrupt();
- return ns;
- }
- }
- else
- Thread.onSpinWait();
- }
- }
- else if (h != null) { // help release stale waiters
- WNode c; Thread w;
- while ((c = h.cowait) != null) {
- if (WCOWAIT.weakCompareAndSet(h, c, c.cowait) &&
- (w = c.thread) != null)
- LockSupport.unpark(w);
- }
- }
- if (whead == h) {
- if ((np = node.prev) != p) {
- if (np != null)
- (p = np).next = node; // stale
- }
- else if ((ps = p.status) == 0)
- WSTATUS.compareAndSet(p, 0, WAITING);
- else if (ps == CANCELLED) {
- if ((pp = p.prev) != null) {
- node.prev = pp;
- pp.next = node;
- }
- }
- else {
- long time; // 0 argument to park means no timeout
- if (deadline == 0L)
- time = 0L;
- else if ((time = deadline - System.nanoTime()) <= 0L)
- return cancelWaiter(node, node, false);
- Thread wt = Thread.currentThread();
- node.thread = wt;
- if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
- whead == h && node.prev == p) {
- if (time == 0L)
- LockSupport.park(this);
- else
- LockSupport.parkNanos(this, time);
- }
- node.thread = null;
- if (Thread.interrupted()) {
- if (interruptible)
- return cancelWaiter(node, node, true);
- wasInterrupted = true;
- }
- }
- }
- }
+ return cancelAcquire(node, interrupted);
}
/**
@@ -1346,182 +1266,178 @@
*
* @param interruptible true if should check interrupts and if so
* return INTERRUPTED
- * @param deadline if nonzero, the System.nanoTime value to timeout
- * at (and return zero)
+ * @param timed if true use timed waits
+ * @param time the System.nanoTime value to timeout at (and return zero)
* @return next state, or INTERRUPTED
*/
- private long acquireRead(boolean interruptible, long deadline) {
- boolean wasInterrupted = false;
- WNode node = null, p;
- for (int spins = -1;;) {
- WNode h;
- if ((h = whead) == (p = wtail)) {
- for (long m, s, ns;;) {
- if ((m = (s = state) & ABITS) < RFULL ?
- casState(s, ns = s + RUNIT) :
- (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
- if (wasInterrupted)
- Thread.currentThread().interrupt();
- return ns;
+ private long acquireRead(boolean interruptible, boolean timed, long time) {
+ boolean interrupted = false;
+ ReaderNode node = null;
+ /*
+ * Loop:
+ * if empty, try to acquire
+ * if tail is Reader, try to cowait; restart if leader stale or cancels
+ * else try to create and enqueue node, and wait in 2nd loop below
+ */
+ for (;;) {
+ ReaderNode leader; long nextState;
+ Node tailPred = null, t = tail;
+ if ((t == null || (tailPred = t.prev) == null) &&
+ (nextState = tryAcquireRead()) != 0L) // try now if empty
+ return nextState;
+ else if (t == null)
+ tryInitializeHead();
+ else if (tailPred == null || !(t instanceof ReaderNode)) {
+ if (node == null)
+ node = new ReaderNode();
+ if (tail == t) {
+ node.setPrevRelaxed(t);
+ if (casTail(t, node)) {
+ t.next = node;
+ break; // node is leader; wait in loop below
}
- else if (m >= WBIT) {
- if (spins > 0) {
- --spins;
- Thread.onSpinWait();
- }
- else {
- if (spins == 0) {
- WNode nh = whead, np = wtail;
- if ((nh == h && np == p) || (h = nh) != (p = np))
- break;
- }
- spins = SPINS;
- }
+ node.setPrevRelaxed(null);
+ }
+ } else if ((leader = (ReaderNode)t) == tail) { // try to cowait
+ for (boolean attached = false;;) {
+ if (leader.status < 0 || leader.prev == null)
+ break;
+ else if (node == null)
+ node = new ReaderNode();
+ else if (node.waiter == null)
+ node.waiter = Thread.currentThread();
+ else if (!attached) {
+ ReaderNode c = leader.cowaiters;
+ node.setCowaitersRelaxed(c);
+ attached = leader.casCowaiters(c, node);
+ if (!attached)
+ node.setCowaitersRelaxed(null);
+ } else {
+ long nanos = 0L;
+ if (!timed)
+ LockSupport.park(this);
+ else if ((nanos = time - System.nanoTime()) > 0L)
+ LockSupport.parkNanos(this, nanos);
+ interrupted |= Thread.interrupted();
+ if ((interrupted && interruptible) ||
+ (timed && nanos <= 0L))
+ return cancelCowaiter(node, leader, interrupted);
}
}
- }
- if (p == null) { // initialize queue
- WNode hd = new WNode(WMODE, null);
- if (WHEAD.weakCompareAndSet(this, null, hd))
- wtail = hd;
- }
- else if (node == null)
- node = new WNode(RMODE, p);
- else if (h == p || p.mode != RMODE) {
- if (node.prev != p)
- node.prev = p;
- else if (WTAIL.weakCompareAndSet(this, p, node)) {
- p.next = node;
- break;
- }
- }
- else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node))
- node.cowait = null;
- else {
- for (;;) {
- WNode pp, c; Thread w;
- if ((h = whead) != null && (c = h.cowait) != null &&
- WCOWAIT.compareAndSet(h, c, c.cowait) &&
- (w = c.thread) != null) // help release
- LockSupport.unpark(w);
- if (Thread.interrupted()) {
- if (interruptible)
- return cancelWaiter(node, p, true);
- wasInterrupted = true;
- }
- if (h == (pp = p.prev) || h == p || pp == null) {
- long m, s, ns;
- do {
- if ((m = (s = state) & ABITS) < RFULL ?
- casState(s, ns = s + RUNIT) :
- (m < WBIT &&
- (ns = tryIncReaderOverflow(s)) != 0L)) {
- if (wasInterrupted)
- Thread.currentThread().interrupt();
- return ns;
- }
- } while (m < WBIT);
- }
- if (whead == h && p.prev == pp) {
- long time;
- if (pp == null || h == p || p.status > 0) {
- node = null; // throw away
- break;
- }
- if (deadline == 0L)
- time = 0L;
- else if ((time = deadline - System.nanoTime()) <= 0L) {
- if (wasInterrupted)
- Thread.currentThread().interrupt();
- return cancelWaiter(node, p, false);
- }
- Thread wt = Thread.currentThread();
- node.thread = wt;
- if ((h != pp || (state & ABITS) == WBIT) &&
- whead == h && p.prev == pp) {
- if (time == 0L)
- LockSupport.park(this);
- else
- LockSupport.parkNanos(this, time);
- }
- node.thread = null;
- }
- }
+ if (node != null)
+ node.waiter = null;
+ long ns = tryAcquireRead();
+ signalCowaiters(leader);
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ if (ns != 0L)
+ return ns;
+ else
+ node = null; // restart if stale, missed, or leader cancelled
}
}
- for (int spins = -1;;) {
- WNode h, np, pp; int ps;
- if ((h = whead) == p) {
- if (spins < 0)
- spins = HEAD_SPINS;
- else if (spins < MAX_HEAD_SPINS)
- spins <<= 1;
- for (int k = spins;;) { // spin at head
- long m, s, ns;
- if ((m = (s = state) & ABITS) < RFULL ?
- casState(s, ns = s + RUNIT) :
- (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
- WNode c; Thread w;
- whead = node;
- node.prev = null;
- while ((c = node.cowait) != null) {
- if (WCOWAIT.compareAndSet(node, c, c.cowait) &&
- (w = c.thread) != null)
- LockSupport.unpark(w);
- }
- if (wasInterrupted)
- Thread.currentThread().interrupt();
- return ns;
- }
- else if (m >= WBIT && --k <= 0)
- break;
- else
- Thread.onSpinWait();
+ // node is leader of a cowait group; almost same as acquireWrite
+ byte spins = 0, postSpins = 0; // retries upon unpark of first thread
+ boolean first = false;
+ Node pred = null;
+ for (long nextState;;) {
+ if (!first && (pred = node.prev) != null &&
+ !(first = (head == pred))) {
+ if (pred.status < 0) {
+ cleanQueue(); // predecessor cancelled
+ continue;
+ } else if (pred.prev == null) {
+ Thread.onSpinWait(); // ensure serialization
+ continue;
}
}
- else if (h != null) {
- WNode c; Thread w;
- while ((c = h.cowait) != null) {
- if (WCOWAIT.compareAndSet(h, c, c.cowait) &&
- (w = c.thread) != null)
- LockSupport.unpark(w);
- }
- }
- if (whead == h) {
- if ((np = node.prev) != p) {
- if (np != null)
- (p = np).next = node; // stale
- }
- else if ((ps = p.status) == 0)
- WSTATUS.compareAndSet(p, 0, WAITING);
- else if (ps == CANCELLED) {
- if ((pp = p.prev) != null) {
- node.prev = pp;
- pp.next = node;
- }
+ if ((first || pred == null) &&
+ (nextState = tryAcquireRead()) != 0L) {
+ if (first) {
+ node.prev = null;
+ head = node;
+ pred.next = null;
+ node.waiter = null;
}
- else {
- long time;
- if (deadline == 0L)
- time = 0L;
- else if ((time = deadline - System.nanoTime()) <= 0L)
- return cancelWaiter(node, node, false);
- Thread wt = Thread.currentThread();
- node.thread = wt;
- if (p.status < 0 &&
- (p != h || (state & ABITS) == WBIT) &&
- whead == h && node.prev == p) {
- if (time == 0L)
- LockSupport.park(this);
- else
- LockSupport.parkNanos(this, time);
+ signalCowaiters(node);
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ return nextState;
+ } else if (first && spins != 0) {
+ --spins;
+ Thread.onSpinWait();
+ } else if (node.status == 0) {
+ if (node.waiter == null)
+ node.waiter = Thread.currentThread();
+ node.status = WAITING;
+ } else {
+ long nanos;
+ spins = postSpins = (byte)((postSpins << 1) | 1);
+ if (!timed)
+ LockSupport.park(this);
+ else if ((nanos = time - System.nanoTime()) > 0L)
+ LockSupport.parkNanos(this, nanos);
+ else
+ break;
+ node.clearStatus();
+ if ((interrupted |= Thread.interrupted()) && interruptible)
+ break;
+ }
+ }
+ return cancelAcquire(node, interrupted);
+ }
+
+ // Cancellation support
+
+ /**
+ * Possibly repeatedly traverses from tail, unsplicing cancelled
+ * nodes until none are found. Unparks nodes that may have been
+ * relinked to be next eligible acquirer.
+ */
+ private void cleanQueue() {
+ for (;;) { // restart point
+ for (Node q = tail, s = null, p, n;;) { // (p, q, s) triples
+ if (q == null || (p = q.prev) == null)
+ return; // end of list
+ if (s == null ? tail != q : (s.prev != q || s.status < 0))
+ break; // inconsistent
+ if (q.status < 0) { // cancelled
+ if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
+ q.prev == p) {
+ p.casNext(q, s); // OK if fails
+ if (p.prev == null)
+ signalNext(p);
}
- node.thread = null;
- if (Thread.interrupted()) {
- if (interruptible)
- return cancelWaiter(node, node, true);
- wasInterrupted = true;
+ break;
+ }
+ if ((n = p.next) != q) { // help finish
+ if (n != null && q.prev == p && q.status >= 0) {
+ p.casNext(n, q);
+ if (p.prev == null)
+ signalNext(p);
+ }
+ break;
+ }
+ s = q;
+ q = q.prev;
+ }
+ }
+ }
+
+ /**
+ * If leader exists, possibly repeatedly traverses cowaiters,
+ * unsplicing the given cancelled node until not found.
+ */
+ private void unlinkCowaiter(ReaderNode node, ReaderNode leader) {
+ if (leader != null) {
+ while (leader.prev != null && leader.status >= 0) {
+ for (ReaderNode p = leader, q; ; p = q) {
+ if ((q = p.cowaiters) == null)
+ return;
+ if (q == node) {
+ p.casCowaiters(q, q.cowaiters);
+ break; // recheck even if succeeded
}
}
}
@@ -1530,105 +1446,53 @@
/**
* If node non-null, forces cancel status and unsplices it from
- * queue if possible and wakes up any cowaiters (of the node, or
- * group, as applicable), and in any case helps release current
- * first waiter if lock is free. (Calling with null arguments
- * serves as a conditional form of release, which is not currently
- * needed but may be needed under possible future cancellation
- * policies). This is a variant of cancellation methods in
- * AbstractQueuedSynchronizer (see its detailed explanation in AQS
- * internal documentation).
+ * queue, wakes up any cowaiters, and possibly wakes up successor
+ * to recheck status.
*
- * @param node if non-null, the waiter
- * @param group either node or the group node is cowaiting with
+ * @param node the waiter (may be null if not yet enqueued)
* @param interrupted if already interrupted
* @return INTERRUPTED if interrupted or Thread.interrupted, else zero
*/
- private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
- if (node != null && group != null) {
- Thread w;
+ private long cancelAcquire(Node node, boolean interrupted) {
+ if (node != null) {
+ node.waiter = null;
node.status = CANCELLED;
- // unsplice cancelled nodes from group
- for (WNode p = group, q; (q = p.cowait) != null;) {
- if (q.status == CANCELLED) {
- WCOWAIT.compareAndSet(p, q, q.cowait);
- p = group; // restart
- }
- else
- p = q;
- }
- if (group == node) {
- for (WNode r = group.cowait; r != null; r = r.cowait) {
- if ((w = r.thread) != null)
- LockSupport.unpark(w); // wake up uncancelled co-waiters
- }
- for (WNode pred = node.prev; pred != null; ) { // unsplice
- WNode succ, pp; // find valid successor
- while ((succ = node.next) == null ||
- succ.status == CANCELLED) {
- WNode q = null; // find successor the slow way
- for (WNode t = wtail; t != null && t != node; t = t.prev)
- if (t.status != CANCELLED)
- q = t; // don't link if succ cancelled
- if (succ == q || // ensure accurate successor
- WNEXT.compareAndSet(node, succ, succ = q)) {
- if (succ == null && node == wtail)
- WTAIL.compareAndSet(this, node, pred);
- break;
- }
- }
- if (pred.next == node) // unsplice pred link
- WNEXT.compareAndSet(pred, node, succ);
- if (succ != null && (w = succ.thread) != null) {
- // wake up succ to observe new pred
- succ.thread = null;
- LockSupport.unpark(w);
- }
- if (pred.status != CANCELLED || (pp = pred.prev) == null)
- break;
- node.prev = pp; // repeat if new pred wrong/cancelled
- WNEXT.compareAndSet(pp, pred, succ);
- pred = pp;
- }
- }
- }
- WNode h; // Possibly release first waiter
- while ((h = whead) != null) {
- long s; WNode q; // similar to release() but check eligibility
- if ((q = h.next) == null || q.status == CANCELLED) {
- for (WNode t = wtail; t != null && t != h; t = t.prev)
- if (t.status <= 0)
- q = t;
- }
- if (h == whead) {
- if (q != null && h.status == 0 &&
- ((s = state) & ABITS) != WBIT && // waiter is eligible
- (s == 0L || q.mode == RMODE))
- release(h);
- break;
- }
+ cleanQueue();
+ if (node instanceof ReaderNode)
+ signalCowaiters((ReaderNode)node);
}
return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
}
- // VarHandle mechanics
- private static final VarHandle STATE;
- private static final VarHandle WHEAD;
- private static final VarHandle WTAIL;
- private static final VarHandle WNEXT;
- private static final VarHandle WSTATUS;
- private static final VarHandle WCOWAIT;
+ /**
+ * If node non-null, forces cancel status and unsplices from
+ * leader's cowaiters list unless/until it is also cancelled.
+ *
+ * @param node if non-null, the waiter
+ * @param leader if non-null, the node heading cowaiters list
+ * @param interrupted if already interrupted
+ * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
+ */
+ private long cancelCowaiter(ReaderNode node, ReaderNode leader,
+ boolean interrupted) {
+ if (node != null) {
+ node.waiter = null;
+ node.status = CANCELLED;
+ unlinkCowaiter(node, leader);
+ }
+ return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
+ }
+
+ // Unsafe
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final long STATE
+ = U.objectFieldOffset(StampedLock.class, "state");
+ private static final long HEAD
+ = U.objectFieldOffset(StampedLock.class, "head");
+ private static final long TAIL
+ = U.objectFieldOffset(StampedLock.class, "tail");
+
static {
- try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATE = l.findVarHandle(StampedLock.class, "state", long.class);
- WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class);
- WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class);
- WSTATUS = l.findVarHandle(WNode.class, "status", int.class);
- WNEXT = l.findVarHandle(WNode.class, "next", WNode.class);
- WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class);
- } catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
- }
+ Class<?> ensureLoaded = LockSupport.class;
}
}
--- a/src/java.base/share/classes/java/util/concurrent/package-info.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/concurrent/package-info.java Wed Sep 18 07:46:02 2019 +0200
@@ -226,9 +226,8 @@
*
* <h2 id="MemoryVisibility">Memory Consistency Properties</h2>
*
- * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-17.html#jls-17.4.5">
* Chapter 17 of
- * <cite>The Java™ Language Specification</cite></a> defines the
+ * <cite>The Java™ Language Specification</cite> defines the
* <i>happens-before</i> relation on memory operations such as reads and
* writes of shared variables. The results of a write by one thread are
* guaranteed to be visible to a read by another thread only if the write
@@ -302,6 +301,8 @@
*
* </ul>
*
+ * @jls 17.4.5 Happens-before Order
+ *
* @since 1.5
*/
package java.util.concurrent;
--- a/src/java.base/share/classes/java/util/regex/Pattern.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/share/classes/java/util/regex/Pattern.java Wed Sep 18 07:46:02 2019 +0200
@@ -3931,12 +3931,14 @@
boolean match(Matcher matcher, int i, CharSequence seq) {
if (i < matcher.to) {
int ch = Character.codePointAt(seq, i);
- return predicate.is(ch) &&
- next.match(matcher, i + Character.charCount(ch), seq);
- } else {
- matcher.hitEnd = true;
- return false;
+ i += Character.charCount(ch);
+ if (i <= matcher.to) {
+ return predicate.is(ch) &&
+ next.match(matcher, i, seq);
+ }
}
+ matcher.hitEnd = true;
+ return false;
}
boolean study(TreeInfo info) {
info.minLength++;
--- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java Wed Sep 18 07:46:02 2019 +0200
@@ -33,7 +33,7 @@
protected UnixNativeDispatcher() { }
// returns a NativeBuffer containing the given path
- private static NativeBuffer copyToNativeBuffer(UnixPath path) {
+ static NativeBuffer copyToNativeBuffer(UnixPath path) {
byte[] cstr = path.getByteArrayForSysCalls();
int size = cstr.length + 1;
NativeBuffer buffer = NativeBuffers.getNativeBufferFromCache(size);
--- a/src/java.base/unix/native/libjava/TimeZone_md.c Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.base/unix/native/libjava/TimeZone_md.c Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -42,6 +42,8 @@
#include "jvm.h"
#include "TimeZone_md.h"
+static char *isFileIdentical(char* buf, size_t size, char *pathname);
+
#define SKIP_SPACE(p) while (*p == ' ' || *p == '\t') p++;
#define RESTARTABLE(_cmd, _result) do { \
@@ -72,6 +74,8 @@
static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";
#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */
+static const char popularZones[][4] = {"UTC", "GMT"};
+
#if defined(_AIX)
static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
#endif
@@ -121,14 +125,27 @@
findZoneinfoFile(char *buf, size_t size, const char *dir)
{
DIR *dirp = NULL;
- struct stat64 statbuf;
struct dirent *dp = NULL;
char *pathname = NULL;
- int fd = -1;
- char *dbuf = NULL;
char *tz = NULL;
int res;
+ if (strcmp(dir, ZONEINFO_DIR) == 0) {
+ /* fast path for 1st iteration */
+ for (unsigned int i = 0; i < sizeof (popularZones) / sizeof (popularZones[0]); i++) {
+ pathname = getPathName(dir, popularZones[i]);
+ if (pathname == NULL) {
+ continue;
+ }
+ tz = isFileIdentical(buf, size, pathname);
+ free((void *) pathname);
+ pathname = NULL;
+ if (tz != NULL) {
+ return tz;
+ }
+ }
+ }
+
dirp = opendir(dir);
if (dirp == NULL) {
return NULL;
@@ -162,58 +179,67 @@
if (pathname == NULL) {
break;
}
- RESTARTABLE(stat64(pathname, &statbuf), res);
- if (res == -1) {
- break;
- }
- if (S_ISDIR(statbuf.st_mode)) {
- tz = findZoneinfoFile(buf, size, pathname);
- if (tz != NULL) {
- break;
- }
- } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
- dbuf = (char *) malloc(size);
- if (dbuf == NULL) {
- break;
- }
- RESTARTABLE(open(pathname, O_RDONLY), fd);
- if (fd == -1) {
- break;
- }
- RESTARTABLE(read(fd, dbuf, size), res);
- if (res != (ssize_t) size) {
- break;
- }
- if (memcmp(buf, dbuf, size) == 0) {
- tz = getZoneName(pathname);
- if (tz != NULL) {
- tz = strdup(tz);
- }
- break;
- }
- free((void *) dbuf);
- dbuf = NULL;
- (void) close(fd);
- fd = -1;
- }
+ tz = isFileIdentical(buf, size, pathname);
free((void *) pathname);
pathname = NULL;
+ if (tz != NULL) {
+ break;
+ }
}
if (dirp != NULL) {
(void) closedir(dirp);
}
- if (pathname != NULL) {
- free((void *) pathname);
+ return tz;
+}
+
+/*
+ * Checks if the file pointed to by pathname matches
+ * the data contents in buf.
+ * Returns a representation of the timezone file name
+ * if file match is found, otherwise NULL.
+ */
+static char *
+isFileIdentical(char *buf, size_t size, char *pathname)
+{
+ char *possibleMatch = NULL;
+ struct stat64 statbuf;
+ char *dbuf = NULL;
+ int fd = -1;
+ int res;
+
+ RESTARTABLE(stat64(pathname, &statbuf), res);
+ if (res == -1) {
+ return NULL;
}
- if (fd != -1) {
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ possibleMatch = findZoneinfoFile(buf, size, pathname);
+ } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
+ dbuf = (char *) malloc(size);
+ if (dbuf == NULL) {
+ return NULL;
+ }
+ RESTARTABLE(open(pathname, O_RDONLY), fd);
+ if (fd == -1) {
+ goto freedata;
+ }
+ RESTARTABLE(read(fd, dbuf, size), res);
+ if (res != (ssize_t) size) {
+ goto freedata;
+ }
+ if (memcmp(buf, dbuf, size) == 0) {
+ possibleMatch = getZoneName(pathname);
+ if (possibleMatch != NULL) {
+ possibleMatch = strdup(possibleMatch);
+ }
+ }
+ freedata:
+ free((void *) dbuf);
(void) close(fd);
}
- if (dbuf != NULL) {
- free((void *) dbuf);
- }
- return tz;
+ return possibleMatch;
}
#if defined(__linux__) || defined(MACOSX)
--- a/src/java.compiler/share/classes/javax/lang/model/package-info.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.compiler/share/classes/javax/lang/model/package-info.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -24,7 +24,8 @@
*/
/**
- * Classes and hierarchies of packages used to model the Java
+ * Types and hierarchies of packages comprising a {@index "Java language
+ * model"}, a model of the declarations and types of the Java
* programming language.
*
* The members of this package and its subpackages are for use in
@@ -41,14 +42,14 @@
* Languages and Applications, October 2004.
* </blockquote>
*
- * In particular, the model makes a distinction between static
+ * In particular, the model makes a distinction between declared
* language constructs, like the {@linkplain javax.lang.model.element
* element} representing {@code java.util.Set}, and the family of
* {@linkplain javax.lang.model.type types} that may be associated
* with an element, like the raw type {@code java.util.Set}, {@code
* java.util.Set<String>}, and {@code java.util.Set<T>}.
*
- * <p> Unless otherwise specified, methods in this package will throw
+ * <p>Unless otherwise specified, methods in this package will throw
* a {@code NullPointerException} if given a {@code null} argument.
*
* @author Joseph D. Darcy
--- a/src/java.desktop/unix/native/common/awt/fontpath.c Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.desktop/unix/native/common/awt/fontpath.c Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -887,9 +887,9 @@
locale = (*env)->GetStringUTFChars(env, localeStr, 0);
if ((libfontconfig = openFontConfig()) == NULL) {
- (*env)->ReleaseStringUTFChars (env, fcNameStr, (const char*)fcName);
+ (*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
- (*env)->ReleaseStringUTFChars (env, localeStr,(const char*)locale);
+ (*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);
}
return -1;
}
@@ -918,9 +918,9 @@
FcPatternGetInteger == NULL ||
FcPatternDestroy == NULL) { /* problem with the library: return. */
- (*env)->ReleaseStringUTFChars (env, fcNameStr, (const char*)fcName);
+ (*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
- (*env)->ReleaseStringUTFChars (env, localeStr,(const char*)locale);
+ (*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);
}
closeFontConfig(libfontconfig, JNI_FALSE);
return -1;
@@ -945,9 +945,9 @@
}
(*FcPatternDestroy)(pattern);
- (*env)->ReleaseStringUTFChars (env, fcNameStr, (const char*)fcName);
+ (*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
- (*env)->ReleaseStringUTFChars (env, localeStr, (const char*)locale);
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
closeFontConfig(libfontconfig, JNI_TRUE);
@@ -1179,6 +1179,9 @@
(*env)->DeleteLocalRef(env, fcNameStr);
if (pattern == NULL) {
closeFontConfig(libfontconfig, JNI_FALSE);
+ if (locale) {
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
+ }
return;
}
@@ -1196,6 +1199,9 @@
if (fontset == NULL) {
(*FcPatternDestroy)(pattern);
closeFontConfig(libfontconfig, JNI_FALSE);
+ if (locale) {
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
+ }
return;
}
@@ -1227,6 +1233,9 @@
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
closeFontConfig(libfontconfig, JNI_FALSE);
+ if (locale) {
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
+ }
return;
}
fontCount = 0;
@@ -1269,6 +1278,9 @@
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
closeFontConfig(libfontconfig, JNI_FALSE);
+ if (locale) {
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
+ }
return;
}
@@ -1323,6 +1335,9 @@
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
closeFontConfig(libfontconfig, JNI_FALSE);
+ if (locale) {
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
+ }
return;
}
(*env)->SetObjectField(env,fcCompFontObj, fcAllFontsID, fcFontArr);
@@ -1384,7 +1399,7 @@
/* release resources and close the ".so" */
if (locale) {
- (*env)->ReleaseStringUTFChars (env, localeStr, (const char*)locale);
+ (*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
closeFontConfig(libfontconfig, JNI_TRUE);
}
--- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -1130,6 +1130,9 @@
const char *pLibName = env->GetStringUTFChars(libName, NULL);
JNU_CHECK_EXCEPTION_RETURN(env, 0);
HINSTANCE libHandle = (HINSTANCE)JDK_LoadSystemLibrary(pLibName);
+ if (pLibName != NULL) {
+ env->ReleaseStringUTFChars(libName, pLibName);
+ }
if (libHandle != NULL) {
UINT fuLoad = (useVGAColors && !IS_WINXP) ? LR_VGACOLOR : 0;
return ptr_to_jlong(LoadImage(libHandle, MAKEINTRESOURCE(iconID),
--- a/src/java.instrument/share/native/libinstrument/JPLISAgent.c Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.instrument/share/native/libinstrument/JPLISAgent.c Wed Sep 18 07:46:02 2019 +0200
@@ -1486,6 +1486,7 @@
platformLen = convertUft8ToPlatformString((char*)utf8Chars, utf8Len, platformChars, MAXPATHLEN);
if (platformLen < 0) {
createAndThrowInternalError(jnienv);
+ (*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);
return;
}
--- a/src/java.naming/share/classes/com/sun/jndi/ldap/DefaultLdapDnsProvider.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.naming/share/classes/com/sun/jndi/ldap/DefaultLdapDnsProvider.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -76,7 +76,7 @@
}
LdapDnsProviderResult res = new LdapDnsProviderResult(domainName, endpoints);
- if (res.getEndpoints().size() == 0 && res.getDomainName().isEmpty()) {
+ if (res.getEndpoints().isEmpty() && res.getDomainName().isEmpty()) {
return Optional.empty();
} else {
return Optional.of(res);
--- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -37,6 +37,8 @@
* The {@code LdapDnsProviderService} is responsible for creating and providing
* access to the registered {@code LdapDnsProvider}s. The {@link ServiceLoader}
* is used to find and register any implementations of {@link LdapDnsProvider}.
+ *
+ * <p> Instances of this class are safe for use by multiple threads.
*/
final class LdapDnsProviderService {
@@ -68,11 +70,11 @@
}
/**
- * Retrieve the singleton static instance of LdapDnsProviderService.
+ * Retrieves the singleton instance of LdapDnsProviderService.
*/
static LdapDnsProviderService getInstance() {
if (service != null) return service;
- synchronized(LOCK) {
+ synchronized (LOCK) {
if (service != null) return service;
service = new LdapDnsProviderService();
}
@@ -80,7 +82,7 @@
}
/**
- * Retrieve result from the first provider that successfully resolves
+ * Retrieves result from the first provider that successfully resolves
* the endpoints. If no results are found when calling installed
* subclasses of {@code LdapDnsProvider} then this method will fall back
* to the {@code DefaultLdapDnsProvider}.
@@ -91,14 +93,15 @@
LdapDnsProviderResult lookupEndpoints(String url, Hashtable<?,?> env)
throws NamingException
{
- Iterator<LdapDnsProvider> iterator = providers.iterator();
+ LdapDnsProviderResult result = null;
Hashtable<?, ?> envCopy = new Hashtable<>(env);
- LdapDnsProviderResult result = null;
-
- while (result == null && iterator.hasNext()) {
- result = iterator.next().lookupEndpoints(url, envCopy)
- .filter(r -> r.getEndpoints().size() > 0)
- .orElse(null);
+ synchronized (LOCK) {
+ Iterator<LdapDnsProvider> iterator = providers.iterator();
+ while (result == null && iterator.hasNext()) {
+ result = iterator.next().lookupEndpoints(url, envCopy)
+ .filter(r -> !r.getEndpoints().isEmpty())
+ .orElse(null);
+ }
}
if (result == null) {
--- a/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -218,7 +218,7 @@
static BOOLEAN
has_oid(gss_const_OID_set set, gss_const_OID oid)
{
- for (int i = 0; i < set->count; i++) {
+ for (size_t i = 0; i < set->count; i++) {
if (is_same_oid(&set->elements[i], oid)) {
return TRUE;
}
@@ -257,7 +257,7 @@
return;
}
PP("gss_OID_set.count is %d", (int)mechs->count);
- for (int i = 0; i < mechs->count; i++) {
+ for (size_t i = 0; i < mechs->count; i++) {
show_oid(&mechs->elements[i]);
}
}
@@ -1584,7 +1584,7 @@
if (set == NULL || *set == GSS_C_NO_OID_SET) {
return GSS_S_COMPLETE;
}
- for (int i = 0; i < (*set)->count; i++) {
+ for (size_t i = 0; i < (*set)->count; i++) {
delete[] (*set)->elements[i].elements;
}
delete[] (*set)->elements;
--- a/src/jdk.hotspot.agent/solaris/native/libsaproc/saproc.cpp Thu Sep 12 15:04:00 2019 +0200
+++ b/src/jdk.hotspot.agent/solaris/native/libsaproc/saproc.cpp Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -692,11 +692,15 @@
char errMsg[ERR_MSG_SIZE];
td_err_e te;
CHECK_EXCEPTION;
+ if (cmdLine_cstr == NULL) {
+ return;
+ }
// some older versions of libproc.so crash when trying to attach 32 bit
// debugger to 64 bit core file. check and throw error.
#ifndef _LP64
- atoi(cmdLine_cstr);
+ errno = 0;
+ strtol(cmdLine_cstr, NULL, 10);
if (errno) {
// core file
int core_fd;
@@ -706,6 +710,7 @@
memcmp(&e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0 &&
e32.e_type == ET_CORE && e32.e_ident[EI_CLASS] == ELFCLASS64) {
close(core_fd);
+ env->ReleaseStringUTFChars(cmdLine, cmdLine_cstr);
THROW_NEW_DEBUGGER_EXCEPTION("debuggee is 64 bit, use java -d64 for debugger");
}
close(core_fd);
@@ -718,6 +723,7 @@
ps_prochandle_t* ph = proc_arg_grab(cmdLine_cstr, (isProcess? PR_ARG_PIDS : PR_ARG_CORES), PGRAB_FORCE, &gcode, NULL);
env->ReleaseStringUTFChars(cmdLine, cmdLine_cstr);
+
if (! ph) {
if (gcode > 0 && gcode < sizeof(proc_arg_grab_errmsgs)/sizeof(const char*)) {
snprintf(errMsg, ERR_MSG_SIZE, "Attach failed : %s", proc_arg_grab_errmsgs[gcode]);
--- a/test/hotspot/jtreg/ProblemList.txt Thu Sep 12 15:04:00 2019 +0200
+++ b/test/hotspot/jtreg/ProblemList.txt Wed Sep 18 07:46:02 2019 +0200
@@ -90,6 +90,7 @@
# :hotspot_runtime
runtime/jni/terminatedThread/TestTerminatedThread.java 8219652 aix-ppc64
+runtime/ReservedStack/ReservedStackTest.java 8231031 generic-all
#############################################################################
@@ -167,7 +168,7 @@
vmTestbase/nsk/jdi/VirtualMachine/redefineClasses/redefineclasses021/TestDescription.java 8065773 generic-all
vmTestbase/nsk/jdi/VirtualMachine/redefineClasses/redefineclasses023/TestDescription.java 8065773 generic-all
-vmTestbase/nsk/jdb/eval/eval001/eval001.java 8212117 generic-all
+vmTestbase/nsk/jdb/eval/eval001/eval001.java 8221503 generic-all
vmTestbase/metaspace/gc/firstGC_10m/TestDescription.java 8208250 generic-all
vmTestbase/metaspace/gc/firstGC_50m/TestDescription.java 8208250 generic-all
@@ -204,4 +205,16 @@
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/SynchronizerLockingThreads/SynchronizerLockingThreads001/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/SynchronizerLockingThreads/SynchronizerLockingThreads002/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/SynchronizerLockingThreads/SynchronizerLockingThreads003/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/SynchronizerLockingThreads/SynchronizerLockingThreads004/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/SynchronizerLockingThreads/SynchronizerLockingThreads005/TestDescription.java 8231032 generic-all
+
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi001/Multi001.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi002/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi003/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi004/TestDescription.java 8231032 generic-all
+vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8231032 generic-all
+
#############################################################################
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/print/PrintCompileQueue.java Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019, Loongson Technology Co. Ltd. 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8230943
+ * @summary possible deadlock was detected when ran with -XX:+CIPrintCompileQueue
+ * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+CIPrintCompileQueue
+ * compiler.print.PrintCompileQueue
+ *
+ */
+
+package compiler.print;
+
+public class PrintCompileQueue {
+ public static void main(String[] args) {
+ System.out.println("Passed");
+ }
+}
--- a/test/hotspot/jtreg/serviceability/sa/TestJmapCore.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/hotspot/jtreg/serviceability/sa/TestJmapCore.java Wed Sep 18 07:46:02 2019 +0200
@@ -98,8 +98,8 @@
: ProcessTools.executeProcess("sh", "-c", "ulimit -c unlimited && "
+ ProcessTools.getCommandLine(pb));
File core;
- String pattern = Platform.isWindows() ? "mdmp" : "core";
- File[] cores = new File(".").listFiles((dir, name) -> name.contains(pattern));
+ String pattern = Platform.isWindows() ? ".*\\.mdmp" : "core(\\.\\d+)?";
+ File[] cores = new File(".").listFiles((dir, name) -> name.matches(pattern));
if (cores.length == 0) {
// /cores/core.$pid might be generated on macosx by default
String pid = output.firstMatch("^(\\d+)" + pidSeparator, 1);
--- a/test/jdk/ProblemList.txt Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/ProblemList.txt Wed Sep 18 07:46:02 2019 +0200
@@ -564,6 +564,8 @@
javax/management/monitor/DerivedGaugeMonitorTest.java 8042211 generic-all
javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java 8042215 generic-all
+java/lang/management/ThreadMXBean/LockedSynchronizers.java 8231032 generic-all
+
############################################################################
# jdk_io
@@ -872,8 +874,6 @@
com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java 8169942 linux-i586,macosx-all,windows-x64
-com/sun/jndi/ldap/LdapTimeoutTest.java 8151678 generic-all
-
com/sun/jndi/dns/ConfigTests/PortUnreachable.java 7164518 macosx-all
javax/rmi/ssl/SSLSocketParametersTest.sh 8162906 generic-all
--- a/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -21,424 +21,487 @@
* questions.
*/
-/**
+/*
* @test
- * @run main/othervm LdapTimeoutTest
- * @bug 7094377 8000487 6176036 7056489
+ * @library /test/lib
+ * lib/
+ * @run testng/othervm LdapTimeoutTest
+ * @bug 7094377 8000487 6176036 7056489 8151678
* @summary Timeout tests for ldap
*/
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import java.io.IOException;
+import java.io.OutputStream;
import java.net.Socket;
-import java.net.ServerSocket;
-import java.net.SocketTimeoutException;
-import java.io.*;
-import javax.naming.*;
-import javax.naming.directory.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
import java.util.List;
-import java.util.Hashtable;
-import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.TimeUnit;
-import javax.net.ssl.SSLHandshakeException;
+import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static jdk.test.lib.Utils.adjustTimeout;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.expectThrows;
+public class LdapTimeoutTest {
+
+ // ------ configure test timeouts here ------
-abstract class LdapTest implements Callable {
+ /*
+ * Practical representation of an infinite timeout.
+ */
+ private static final long INFINITY_MILLIS = adjustTimeout(20_000);
+ /*
+ * The acceptable variation in timeout measurements.
+ */
+ private static final long TOLERANCE = adjustTimeout( 3_500);
- Hashtable env;
- TestServer server;
- ScheduledExecutorService killSwitchPool;
- boolean passed = false;
- private int HANGING_TEST_TIMEOUT = 20_000;
+ private static final long CONNECT_MILLIS = adjustTimeout( 3_000);
+ private static final long READ_MILLIS = adjustTimeout(10_000);
+
+ static {
+ // a series of checks to make sure this timeouts configuration is
+ // consistent and the timeouts do not overlap
- public LdapTest (TestServer server, Hashtable env) {
- this.server = server;
- this.env = env;
+ assert (TOLERANCE >= 0);
+ // context creation
+ assert (2 * CONNECT_MILLIS + TOLERANCE < READ_MILLIS);
+ // context creation immediately followed by search
+ assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS);
+ }
+
+ @BeforeTest
+ public void beforeTest() {
+ startAuxiliaryDiagnosticOutput();
}
- public LdapTest(TestServer server, Hashtable env,
- ScheduledExecutorService killSwitchPool)
- {
- this(server, env);
- this.killSwitchPool = killSwitchPool;
+ /*
+ * These are timeout tests and they are run in parallel to reduce the total
+ * amount of run time.
+ *
+ * Currently it doesn't seem possible to instruct JTREG to run TestNG test
+ * methods in parallel. That said, this JTREG test is still
+ * a "TestNG-flavored" test for the sake of having org.testng.Assert
+ * capability.
+ */
+ @Test
+ public void test() throws Exception {
+ List<Future<?>> futures = new ArrayList<>();
+ ExecutorService executorService = Executors.newCachedThreadPool();
+ try {
+ futures.add(executorService.submit(() -> { test1(); return null; }));
+ futures.add(executorService.submit(() -> { test2(); return null; }));
+ futures.add(executorService.submit(() -> { test3(); return null; }));
+ futures.add(executorService.submit(() -> { test4(); return null; }));
+ futures.add(executorService.submit(() -> { test5(); return null; }));
+ futures.add(executorService.submit(() -> { test6(); return null; }));
+ futures.add(executorService.submit(() -> { test7(); return null; }));
+ } finally {
+ executorService.shutdown();
+ }
+ int failedCount = 0;
+ for (var f : futures) {
+ try {
+ f.get();
+ } catch (ExecutionException e) {
+ failedCount++;
+ e.getCause().printStackTrace(System.out);
+ }
+ }
+ if (failedCount > 0)
+ throw new RuntimeException(failedCount + " (sub)tests failed");
+ }
+
+ static void test1() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ // Here and in the other tests it's important to close the server as
+ // calling `thread.interrupt` from assertion may not be enough
+ // (depending on where the blocking call has stuck)
+ try (TestServer server = new NotBindableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ // Here and in the other tests joining done purely to reduce timing
+ // jitter. Commenting out or removing that should not make the test
+ // incorrect. (ServerSocket can accept connection as soon as it is
+ // bound, not need to call `accept` before that.)
+ server.starting().join();
+ assertIncompletion(INFINITY_MILLIS, () -> new InitialDirContext(env));
+ }
}
- public abstract void performOp(InitialContext ctx) throws NamingException;
- public abstract void handleNamingException(
- NamingException e, long start, long end);
+ static void test2() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
+ try (TestServer server = new BindableButNotReadableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ InitialDirContext ctx = new InitialDirContext(env);
+ SearchControls scl = new SearchControls();
+ scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ assertIncompletion(INFINITY_MILLIS,
+ () -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));
+ }
+ }
- public void pass() {
- this.passed = true;
+ static void test3() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ try (TestServer server = new BindableButNotReadableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ InitialDirContext ctx = new InitialDirContext(env);
+ SearchControls scl = new SearchControls();
+ scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ assertIncompletion(INFINITY_MILLIS,
+ () -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));
+ }
}
- public void fail() {
- throw new RuntimeException("Test failed");
+ static void test4() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
+ env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));
+ try (TestServer server = new NotBindableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ Assert.ThrowingRunnable completion =
+ () -> assertCompletion(CONNECT_MILLIS,
+ 2 * CONNECT_MILLIS + TOLERANCE,
+ () -> new InitialDirContext(env));
+ NamingException e = expectThrows(NamingException.class, completion);
+ String msg = e.getMessage();
+ assertTrue(msg != null && msg.contains("timeout")
+ && msg.contains(String.valueOf(CONNECT_MILLIS)),
+ msg);
+ }
}
- public void fail(Exception e) {
- throw new RuntimeException("Test failed", e);
+ static void test5() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
+ env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));
+ try (TestServer server = new BindableButNotReadableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ InitialDirContext ctx = new InitialDirContext(env);
+ SearchControls scl = new SearchControls();
+ scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ Assert.ThrowingRunnable completion =
+ () -> assertCompletion(READ_MILLIS,
+ READ_MILLIS + TOLERANCE,
+ () -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl));
+ NamingException e = expectThrows(NamingException.class, completion);
+ String msg = e.getMessage();
+ assertTrue(msg != null && msg.contains("timeout")
+ && msg.contains(String.valueOf(READ_MILLIS)),
+ msg);
+ }
}
- boolean shutItDown(InitialContext ctx) {
- try {
- if (ctx != null) ctx.close();
- return true;
- } catch (NamingException ex) {
- return false;
+ static void test6() throws Exception {
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
+ env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));
+ try (TestServer server = new NotBindableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ Assert.ThrowingRunnable completion =
+ () -> assertCompletion(CONNECT_MILLIS,
+ 2 * CONNECT_MILLIS + TOLERANCE,
+ () -> new InitialDirContext(env));
+ NamingException e = expectThrows(NamingException.class, completion);
+ String msg = e.getMessage();
+ assertTrue(msg != null && msg.contains("timeout")
+ && msg.contains(String.valueOf(CONNECT_MILLIS)),
+ msg);
}
}
- public Boolean call() {
- InitialContext ctx = null;
- ScheduledFuture killer = null;
- long start = System.nanoTime();
-
- try {
- while(!server.accepting())
- Thread.sleep(200); // allow the server to start up
- Thread.sleep(200); // to be sure
-
- // if this is a hanging test, scheduled a thread to
- // interrupt after a certain time
- if (killSwitchPool != null) {
- final Thread current = Thread.currentThread();
- killer = killSwitchPool.schedule(
- new Callable<Void>() {
- public Void call() throws Exception {
- current.interrupt();
- return null;
- }
- }, HANGING_TEST_TIMEOUT, MILLISECONDS);
- }
-
- env.put(Context.PROVIDER_URL, "ldap://localhost:" +
- server.getLocalPort());
-
- try {
- ctx = new InitialDirContext(env);
- performOp(ctx);
- fail();
- } catch (NamingException e) {
- long end = System.nanoTime();
- System.out.println(this.getClass().toString() + " - elapsed: "
- + NANOSECONDS.toMillis(end - start));
- handleNamingException(e, start, end);
- } finally {
- if (killer != null && !killer.isDone())
- killer.cancel(true);
- shutItDown(ctx);
- server.close();
- }
- return passed;
- } catch (IOException|InterruptedException e) {
- throw new RuntimeException(e);
+ static void test7() throws Exception {
+ // 8000487: Java JNDI connection library on ldap conn is
+ // not honoring configured timeout
+ Hashtable<Object, Object> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
+ env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, "user");
+ env.put(Context.SECURITY_CREDENTIALS, "password");
+ try (TestServer server = new NotBindableServer()) {
+ env.put(Context.PROVIDER_URL, urlTo(server));
+ server.start();
+ server.starting().join();
+ Assert.ThrowingRunnable completion =
+ () -> assertCompletion(CONNECT_MILLIS,
+ 2 * CONNECT_MILLIS + TOLERANCE,
+ () -> new InitialDirContext(env));
+ NamingException e = expectThrows(NamingException.class, completion);
+ String msg = e.getMessage();
+ assertTrue(msg != null && msg.contains("timeout")
+ && msg.contains(String.valueOf(CONNECT_MILLIS)),
+ msg);
}
}
-}
-
-abstract class ReadServerTest extends LdapTest {
-
- public ReadServerTest(Hashtable env) throws IOException {
- super(new BindableServer(), env);
- }
-
- public ReadServerTest(Hashtable env,
- ScheduledExecutorService killSwitchPool)
- throws IOException
- {
- super(new BindableServer(), env, killSwitchPool);
- }
-
- public void performOp(InitialContext ctx) throws NamingException {
- SearchControls scl = new SearchControls();
- scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
- NamingEnumeration<SearchResult> answer = ((InitialDirContext)ctx)
- .search("ou=People,o=JNDITutorial", "(objectClass=*)", scl);
- }
-}
-
-abstract class DeadServerTest extends LdapTest {
-
- public DeadServerTest(Hashtable env) throws IOException {
- super(new DeadServer(), env);
- }
-
- public DeadServerTest(Hashtable env,
- ScheduledExecutorService killSwitchPool)
- throws IOException
- {
- super(new DeadServer(), env, killSwitchPool);
- }
-
- public void performOp(InitialContext ctx) throws NamingException {}
-}
-
-class DeadServerNoTimeoutTest extends DeadServerTest {
-
- public DeadServerNoTimeoutTest(Hashtable env,
- ScheduledExecutorService killSwitchPool)
- throws IOException
- {
- super(env, killSwitchPool);
- }
-
- public void handleNamingException(NamingException e, long start, long end) {
- if (e instanceof InterruptedNamingException) Thread.interrupted();
-
- if (NANOSECONDS.toMillis(end - start) < LdapTimeoutTest.MIN_TIMEOUT) {
- System.err.printf("DeadServerNoTimeoutTest fail: timeout should be " +
- "at least %s ms, actual time is %s ms%n",
- LdapTimeoutTest.MIN_TIMEOUT,
- NANOSECONDS.toMillis(end - start));
- fail();
- } else {
- pass();
- }
- }
-}
-
-class DeadServerTimeoutTest extends DeadServerTest {
-
- public DeadServerTimeoutTest(Hashtable env) throws IOException {
- super(env);
- }
- public void handleNamingException(NamingException e, long start, long end)
- {
- // non SSL connect will timeout via readReply using connectTimeout
- if (NANOSECONDS.toMillis(end - start) < 2_900) {
- pass();
- } else {
- System.err.println("Fail: Waited too long");
- fail();
- }
- }
-}
+ // ------ test stub servers ------
+
+ static class TestServer extends BaseLdapServer {
-
-class ReadServerNoTimeoutTest extends ReadServerTest {
+ private final CompletableFuture<Void> starting = new CompletableFuture<>();
- public ReadServerNoTimeoutTest(Hashtable env,
- ScheduledExecutorService killSwitchPool)
- throws IOException
- {
- super(env, killSwitchPool);
- }
-
- public void handleNamingException(NamingException e, long start, long end) {
- if (e instanceof InterruptedNamingException) Thread.interrupted();
+ TestServer() throws IOException { }
- if (NANOSECONDS.toMillis(end - start) < LdapTimeoutTest.MIN_TIMEOUT) {
- System.err.printf("ReadServerNoTimeoutTest fail: timeout should be " +
- "at least %s ms, actual time is %s ms%n",
- LdapTimeoutTest.MIN_TIMEOUT,
- NANOSECONDS.toMillis(end - start));
- fail();
- } else {
- pass();
+ @Override
+ protected void beforeAcceptingConnections() {
+ starting.completeAsync(() -> null);
}
- }
-}
-class ReadServerTimeoutTest extends ReadServerTest {
-
- public ReadServerTimeoutTest(Hashtable env) throws IOException {
- super(env);
- }
-
- public void handleNamingException(NamingException e, long start, long end) {
- System.out.println("ReadServerTimeoutTest: end-start=" + NANOSECONDS.toMillis(end - start));
- if (NANOSECONDS.toMillis(end - start) < 2_500) {
- fail();
- } else {
- pass();
+ public CompletableFuture<Void> starting() {
+ return starting.copy();
}
}
-}
+
+ static class BindableButNotReadableServer extends TestServer {
+
+ BindableButNotReadableServer() throws IOException { }
-class TestServer extends Thread {
- ServerSocket serverSock;
- boolean accepting = false;
-
- public TestServer() throws IOException {
- this.serverSock = new ServerSocket(0);
- start();
- }
+ private static final byte[] bindResponse = {
+ 0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,
+ 0x01, 0x00, 0x04, 0x00, 0x04, 0x00
+ };
- public int getLocalPort() {
- return serverSock.getLocalPort();
- }
-
- public boolean accepting() {
- return accepting;
- }
-
- public void close() throws IOException {
- serverSock.close();
- }
-}
-
-class BindableServer extends TestServer {
-
- public BindableServer() throws IOException {
- super();
+ @Override
+ protected void handleRequest(Socket socket,
+ LdapMessage msg,
+ OutputStream out)
+ throws IOException {
+ switch (msg.getOperation()) {
+ case BIND_REQUEST:
+ out.write(bindResponse);
+ out.flush();
+ default:
+ break;
+ }
+ }
}
- private byte[] bindResponse = {
- 0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,
- 0x01, 0x00, 0x04, 0x00, 0x04, 0x00
- };
+ static class NotBindableServer extends TestServer {
- public void run() {
- try {
- accepting = true;
- Socket socket = serverSock.accept();
- InputStream in = socket.getInputStream();
- OutputStream out = socket.getOutputStream();
-
- // Read the LDAP BindRequest
- while (in.read() != -1) {
- in.skip(in.available());
- break;
- }
+ NotBindableServer() throws IOException { }
- // Write an LDAP BindResponse
- out.write(bindResponse);
- out.flush();
- } catch (IOException e) {
- // ignore
- }
- }
-}
-
-class DeadServer extends TestServer {
-
- public DeadServer() throws IOException {
- super();
- }
-
- public void run() {
- while(true) {
+ @Override
+ protected void beforeConnectionHandled(Socket socket) {
try {
- accepting = true;
- Socket socket = serverSock.accept();
- } catch (Exception e) {
- break;
+ TimeUnit.DAYS.sleep(Integer.MAX_VALUE);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
}
}
-}
-public class LdapTimeoutTest {
+ // ------ timeouts check utilities ------
- private static final ExecutorService testPool =
- Executors.newFixedThreadPool(3);
- private static final ScheduledExecutorService killSwitchPool =
- Executors.newScheduledThreadPool(3);
- public static int MIN_TIMEOUT = 18_000;
+ /*
+ * Asserts that the specified executable yields a result or an exception
+ * within the specified time frame. Interrupts the executable
+ * unconditionally.
+ *
+ * If the executable yields a result or an exception within the specified
+ * time frame, the result will be returned and the exception will be
+ * rethrown respectively in a transparent fashion as if the executable was
+ * executed directly.
+ */
+ public static <T> T assertCompletion(long loMillis,
+ long hiMillis,
+ Callable<T> code)
+ throws Throwable {
+ if (loMillis < 0 || hiMillis < 0 || loMillis > hiMillis) {
+ throw new IllegalArgumentException("loMillis=" + loMillis +
+ ", hiMillis=" + hiMillis);
+ }
+ Objects.requireNonNull(code);
- static Hashtable createEnv() {
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- return env;
- }
+ // this queue acts both as an exchange point and a barrier
+ SynchronousQueue<Long> startTime = new SynchronousQueue<>();
+
+ Callable<T> wrappedTask = () -> {
+ // by the time this value reaches the "stopwatch" thread it might be
+ // well outdated and that's okay, we will adjust the wait time
+ startTime.put(System.nanoTime());
+ return code.call();
+ };
- public static void main(String[] args) throws Exception {
+ FutureTask<T> task = new FutureTask<>(wrappedTask);
+ Thread t = new Thread(task);
+ t.start();
- InitialContext ctx = null;
- List<Future> results = new ArrayList<>();
+ final long startNanos;
+ try {
+ startNanos = startTime.take(); // (1) wait for the initial time mark
+ } catch (Throwable e) {
+ t.interrupt();
+ throw e;
+ }
+
+ final long waitTime = hiMillis -
+ NANOSECONDS.toMillis(System.nanoTime() - startNanos); // (2) adjust wait time
try {
- // run the DeadServerTest with no timeouts set
- // this should get stuck indefinitely, so we need to kill
- // it after a timeout
- System.out.println("Running connect timeout test with 20s kill switch");
- Hashtable env = createEnv();
- results.add(
- testPool.submit(new DeadServerNoTimeoutTest(env, killSwitchPool)));
-
- // run the ReadServerTest with connect timeout set
- // this should get stuck indefinitely so we need to kill
- // it after a timeout
- System.out.println("Running read timeout test with 10ms connect timeout & 20s kill switch");
- Hashtable env1 = createEnv();
- env1.put("com.sun.jndi.ldap.connect.timeout", "10");
- results.add(testPool.submit(
- new ReadServerNoTimeoutTest(env1, killSwitchPool)));
-
- // run the ReadServerTest with no timeouts set
- // this should get stuck indefinitely, so we need to kill
- // it after a timeout
- System.out.println("Running read timeout test with 20s kill switch");
- Hashtable env2 = createEnv();
- results.add(testPool.submit(
- new ReadServerNoTimeoutTest(env2, killSwitchPool)));
-
- // run the DeadServerTest with connect / read timeouts set
- // this should exit after the connect timeout expires
- System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout");
- Hashtable env3 = createEnv();
- env3.put("com.sun.jndi.ldap.connect.timeout", "10");
- env3.put("com.sun.jndi.ldap.read.timeout", "3000");
- results.add(testPool.submit(new DeadServerTimeoutTest(env3)));
-
-
- // run the ReadServerTest with connect / read timeouts set
- // this should exit after the connect timeout expires
- //
- // NOTE: commenting this test out as it is failing intermittently.
- //
- // System.out.println("Running read timeout test with 10ms connect timeout, 3000ms read timeout");
- // Hashtable env4 = createEnv();
- // env4.put("com.sun.jndi.ldap.connect.timeout", "10");
- // env4.put("com.sun.jndi.ldap.read.timeout", "3000");
- // results.add(testPool.submit(new ReadServerTimeoutTest(env4)));
-
- // run the DeadServerTest with connect timeout set
- // this should exit after the connect timeout expires
- System.out.println("Running connect timeout test with 10ms connect timeout");
- Hashtable env5 = createEnv();
- env5.put("com.sun.jndi.ldap.connect.timeout", "10");
- results.add(testPool.submit(new DeadServerTimeoutTest(env5)));
-
- // 8000487: Java JNDI connection library on ldap conn is
- // not honoring configured timeout
- System.out.println("Running simple auth connection test");
- Hashtable env6 = createEnv();
- env6.put("com.sun.jndi.ldap.connect.timeout", "10");
- env6.put("com.sun.jndi.ldap.read.timeout", "3000");
- env6.put(Context.SECURITY_AUTHENTICATION, "simple");
- env6.put(Context.SECURITY_PRINCIPAL, "user");
- env6.put(Context.SECURITY_CREDENTIALS, "password");
- results.add(testPool.submit(new DeadServerTimeoutTest(env6)));
-
- boolean testFailed = false;
- for (Future test : results) {
- while (!test.isDone()) {
- if ((Boolean) test.get() == false)
- testFailed = true;
- }
+ T r = task.get(waitTime, MILLISECONDS); // (3) wait for the task to complete
+ long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ if (elapsed < loMillis || elapsed > hiMillis) {
+ throw new RuntimeException(format(
+ "After %s ms. (waitTime %s ms.) returned result '%s'", elapsed, waitTime, r));
}
-
- if (testFailed) {
- throw new AssertionError("some tests failed");
+ return r;
+ } catch (ExecutionException e) {
+ long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ if (elapsed < loMillis || elapsed > hiMillis) {
+ throw new RuntimeException(format(
+ "After %s ms. (waitTime %s ms.) thrown exception", elapsed, waitTime), e);
}
-
+ throw e.getCause();
+ } catch (TimeoutException e) {
+ // We trust timed get not to throw TimeoutException prematurely
+ // (i.e. before the wait time elapses)
+ long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ throw new RuntimeException(format(
+ "After %s ms. (waitTime %s ms.) is incomplete", elapsed, waitTime));
} finally {
- LdapTimeoutTest.killSwitchPool.shutdown();
- LdapTimeoutTest.testPool.shutdown();
+ t.interrupt();
}
}
+ /*
+ * Asserts that the specified executable yields no result and no exception
+ * for at least the specified amount of time. Interrupts the executable
+ * unconditionally.
+ */
+ public static void assertIncompletion(long millis, Callable<?> code)
+ throws Exception
+ {
+ if (millis < 0) {
+ throw new IllegalArgumentException("millis=" + millis);
+ }
+ Objects.requireNonNull(code);
+
+ // this queue acts both as an exchange point and a barrier
+ SynchronousQueue<Long> startTime = new SynchronousQueue<>();
+
+ Callable<?> wrappedTask = () -> {
+ // by the time this value reaches the "stopwatch" thread it might be
+ // well outdated and that's okay, we will adjust the wait time
+ startTime.put(System.nanoTime());
+ return code.call();
+ };
+
+ FutureTask<?> task = new FutureTask<>(wrappedTask);
+ Thread t = new Thread(task);
+ t.start();
+
+ final long startNanos;
+ try {
+ startNanos = startTime.take(); // (1) wait for the initial time mark
+ } catch (Throwable e) {
+ t.interrupt();
+ throw e;
+ }
+
+ final long waitTime = millis -
+ NANOSECONDS.toMillis(System.nanoTime() - startNanos); // (2) adjust wait time
+
+ try {
+ Object r = task.get(waitTime, MILLISECONDS); // (3) wait for the task to complete
+ long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ if (elapsed < waitTime) {
+ throw new RuntimeException(format(
+ "After %s ms. (waitTime %s ms.) returned result '%s'", elapsed, waitTime, r));
+ }
+ } catch (ExecutionException e) {
+ long elapsed = NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ if (elapsed < waitTime) {
+ throw new RuntimeException(format(
+ "After %s ms. (waitTime %s ms.) thrown exception", elapsed, waitTime), e);
+ }
+ } catch (TimeoutException expected) {
+ } finally {
+ t.interrupt();
+ }
+ }
+
+ // ------ miscellaneous utilities ------
+
+ private static String urlTo(TestServer server) {
+ String hostAddress = server.getInetAddress().getHostAddress();
+ String addr;
+ if (hostAddress.contains(":")) { // IPv6
+ addr = '[' + hostAddress + ']';
+ } else { // IPv4
+ addr = hostAddress;
+ }
+ return "ldap://" + addr + ":" + server.getPort();
+ }
+
+ /*
+ * A diagnostic aid that might help with debugging timeout issues. The idea
+ * is to continuously measure accuracy and responsiveness of the system that
+ * runs this test. If the system is overwhelmed (with something else), it
+ * might affect the test run. At the very least we will have traces of that
+ * in the logs.
+ *
+ * This utility does not automatically scale up test timeouts, it simply
+ * gathers information.
+ */
+ private static void startAuxiliaryDiagnosticOutput() {
+ System.out.printf("Starting diagnostic output (probe)%n");
+ Thread t = new Thread(() -> {
+ for (int i = 0; ; i = ((i % 20) + 1)) {
+ // 500, 1_000, 1_500, ..., 9_500, 10_000, 500, 1_000, ...
+ long expected = i * 500;
+ long start = System.nanoTime();
+ try {
+ MILLISECONDS.sleep(expected);
+ } catch (InterruptedException e) {
+ return;
+ }
+ long stop = System.nanoTime();
+ long actual = NANOSECONDS.toMillis(stop - start);
+ System.out.printf("(probe) expected [ms.]: %s, actual [ms.]: %s%n",
+ expected, actual);
+
+ }
+ }, "probe");
+ t.setDaemon(true);
+ t.start();
+ }
}
-
--- a/test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java Wed Sep 18 07:46:02 2019 +0200
@@ -35,7 +35,6 @@
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
import static java.lang.System.Logger.Level.INFO;
@@ -44,6 +43,7 @@
*
* Override the following methods to provide customized behavior
*
+ * * beforeAcceptingConnections
* * beforeConnectionHandled
* * handleRequest
*
@@ -83,6 +83,7 @@
logger().log(INFO, "Server is accepting connections at port {0}",
getPort());
try {
+ beforeAcceptingConnections();
while (isRunning()) {
Socket socket = serverSocket.accept();
logger().log(INFO, "Accepted new connection at {0}", socket);
@@ -97,10 +98,10 @@
}
connectionsPool.submit(() -> handleConnection(socket));
}
- } catch (IOException | RejectedExecutionException e) {
+ } catch (Throwable t) {
if (isRunning()) {
throw new RuntimeException(
- "Unexpected exception while accepting connections", e);
+ "Unexpected exception while accepting connections", t);
}
} finally {
logger().log(INFO, "Server stopped accepting connections at port {0}",
@@ -109,6 +110,13 @@
}
/*
+ * Called once immediately preceding the server accepting connections.
+ *
+ * Override to customize the behavior.
+ */
+ protected void beforeAcceptingConnections() { }
+
+ /*
* A "Template Method" describing how a connection (represented by a socket)
* is handled.
*
@@ -240,12 +248,25 @@
/**
* Returns the local port this server is listening at.
*
+ * This method can be called at any time.
+ *
* @return the port this server is listening at
*/
public int getPort() {
return serverSocket.getLocalPort();
}
+ /**
+ * Returns the address this server is listening at.
+ *
+ * This method can be called at any time.
+ *
+ * @return the address
+ */
+ public InetAddress getInetAddress() {
+ return serverSocket.getInetAddress();
+ }
+
/*
* Returns a flag to indicate whether this server is running or not.
*
--- a/test/jdk/java/net/CookieHandler/CookieManagerTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/net/CookieHandler/CookieManagerTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -26,7 +26,8 @@
* @summary Unit test for java.net.CookieManager
* @bug 6244040 7150552 7051862
* @modules jdk.httpserver
- * @run main/othervm -ea CookieManagerTest
+ * java.logging
+ * @run main/othervm -ea -esa CookieManagerTest
* @author Edward Wang
*/
@@ -36,6 +37,8 @@
import java.util.List;
import java.io.IOException;
import java.net.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import static java.net.Proxy.NO_PROXY;
public class CookieManagerTest {
@@ -63,6 +66,11 @@
}
public static void main(String[] args) throws Exception {
+ // logs everything...
+ Logger root = Logger.getLogger("");
+ root.setLevel(Level.ALL);
+ root.getHandlers()[0].setLevel(Level.ALL);
+
startHttpServer();
makeHttpCall();
--- a/test/jdk/java/net/Socket/HttpProxy.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/net/Socket/HttpProxy.java Wed Sep 18 07:46:02 2019 +0200
@@ -160,6 +160,7 @@
public void run() {
try { simpleWrite(os, start); }
catch (Exception e) {unexpected(e); }
+ finally { out.println(threadName + ": done"); }
}}, threadName)).start();
}
@@ -170,6 +171,7 @@
b[1] = (byte) (i % 256);
os.write(b);
}
+ out.println("Wrote " + start + " -> " + (start + 100));
}
void simpleRead(InputStream is, int start) throws Exception {
@@ -184,6 +186,7 @@
if (r != i)
throw new Exception("read " + r + " expected " +i);
}
+ out.println("Read " + start + " -> " + (start + 100));
}
int bytes(byte b1, byte b2) {
@@ -249,6 +252,7 @@
// retrieve the host and port info from the status-line
InetSocketAddress serverAddr = getConnectInfo(statusLine);
+ out.println("Proxy serving CONNECT request to " + serverAddr);
//open socket to the server
try (Socket serverSocket = new Socket(serverAddr.getAddress(),
--- a/test/jdk/java/net/Socket/NullHost.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/net/Socket/NullHost.java Wed Sep 18 07:46:02 2019 +0200
@@ -46,8 +46,10 @@
return svr.getLocalPort();
}
+ volatile boolean done;
public void shutdown() {
try {
+ done = true;
svr.close();
} catch (IOException e) {
}
@@ -56,11 +58,12 @@
public void run() {
Socket s;
try {
- while (true) {
+ while (!done) {
s = svr.accept();
s.close();
}
} catch (IOException e) {
+ if (!done) e.printStackTrace();
}
}
}
@@ -74,13 +77,9 @@
int port = s.getPort();
s.start();
try {
- Socket sock = new Socket((String)null, port);
- sock.close();
- sock = new Socket((String)null, port, true);
- sock.close();
- sock = new Socket((String)null, port, null, 0);
- sock.close();
-
+ try (var sock = new Socket((String)null, port)) {}
+ try (var sock = new Socket((String)null, port, true)) {}
+ try (var sock = new Socket((String)null, port, null, 0)) {}
} catch (NullPointerException e) {
throw new RuntimeException("Got a NPE");
} finally {
--- a/test/jdk/java/util/Map/Get.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/Map/Get.java Wed Sep 18 07:46:02 2019 +0200
@@ -120,8 +120,8 @@
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() { passed++; }
- static void fail() { failed++; (new Error("Failure")).printStackTrace(System.err); }
- static void fail(String msg) { failed++; (new Error("Failure: " + msg)).printStackTrace(System.err); }
+ static void fail() { failed++; new Error("Failure").printStackTrace(System.err); }
+ static void fail(String msg) { failed++; new Error("Failure: " + msg).printStackTrace(System.err); }
static void unexpected(String msg, Throwable t) { System.err.println("Unexpected: " + msg); unexpected(t); }
static void unexpected(Throwable t) { failed++; t.printStackTrace(System.err); }
static void check(boolean cond) { if (cond) pass(); else fail(); }
--- a/test/jdk/java/util/concurrent/BlockingQueue/OfferDrainToLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/BlockingQueue/OfferDrainToLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -34,6 +34,7 @@
/*
* @test
* @bug 6805775 6815766
+ * @library /test/lib
* @run main OfferDrainToLoops 100
* @summary Test concurrent offer vs. drainTo
*/
@@ -47,10 +48,12 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicLong;
+import jdk.test.lib.Utils;
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
public class OfferDrainToLoops {
- final long testDurationMillisDefault = 10L * 1000L;
+ static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
+ final long testDurationMillisDefault = 10_000L;
final long testDurationMillis;
OfferDrainToLoops(String[] args) {
@@ -76,7 +79,6 @@
System.out.println(q.getClass().getSimpleName());
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
- final long timeoutMillis = 10L * 1000L;
final SplittableRandom rnd = new SplittableRandom();
// Poor man's bounded buffer.
@@ -155,13 +157,13 @@
}}};
for (Thread thread : new Thread[] { offerer, drainer, scanner }) {
- thread.join(timeoutMillis + testDurationMillis);
+ thread.join(LONG_DELAY_MS + testDurationMillis);
if (thread.isAlive()) {
System.err.printf("Hung thread: %s%n", thread.getName());
failed++;
for (StackTraceElement e : thread.getStackTrace())
System.err.println(e);
- thread.join(timeoutMillis);
+ thread.join(LONG_DELAY_MS);
}
}
}
--- a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapCheck.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapCheck.java Wed Sep 18 07:46:02 2019 +0200
@@ -537,7 +537,7 @@
static void printStats() {
for (Iterator it = accum.entrySet().iterator(); it.hasNext(); ) {
Map.Entry e = (Map.Entry)(it.next());
- Stats stats = ((Stats)(e.getValue()));
+ Stats stats = (Stats)(e.getValue());
int n = stats.number;
double t;
if (n > 0)
--- a/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -99,8 +99,8 @@
nops = Integer.parseInt(args[5]);
// normalize probabilities wrt random number generator
- removesPerMaxRandom = (int)(((double)premove/100.0 * 0x7FFFFFFFL));
- insertsPerMaxRandom = (int)(((double)pinsert/100.0 * 0x7FFFFFFFL));
+ removesPerMaxRandom = (int)((double)premove/100.0 * 0x7FFFFFFFL);
+ insertsPerMaxRandom = (int)((double)pinsert/100.0 * 0x7FFFFFFFL);
System.out.print("Class: " + mapClass.getName());
System.out.print(" threads: " + maxThreads);
@@ -172,7 +172,7 @@
long time = timer.getTime();
long tpo = time / (i * (long)nops);
System.out.print(LoopHelpers.rightJustify(tpo) + " ns per op");
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
map.clear();
}
--- a/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java Wed Sep 18 07:46:02 2019 +0200
@@ -24,37 +24,40 @@
/*
* @test
* @bug 4486658 8010293
- * @summary thread safety of toArray methods of subCollections
+ * @summary thread safety of toArray methods of collection views
* @author Martin Buchholz
*/
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ToArray {
public static void main(String[] args) throws Throwable {
- // Execute a number of times to increase the probability of
- // failure if there is an issue
- for (int i = 0; i < 16; i++) {
+ final int runsPerTest = Integer.getInteger("jsr166.runsPerTest", 1);
+ final int reps = 10 * runsPerTest;
+ for (int i = reps; i--> 0; )
executeTest();
- }
}
static void executeTest() throws Throwable {
- final Throwable[] throwable = new Throwable[1];
final ConcurrentHashMap<Integer, Integer> m = new ConcurrentHashMap<>();
-
- // Number of workers equal to the number of processors
- // Each worker will put globally unique keys into the map
- final int nWorkers = Runtime.getRuntime().availableProcessors();
+ final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ final int nCPU = Runtime.getRuntime().availableProcessors();
+ final int minWorkers = 2;
+ final int maxWorkers = Math.max(minWorkers, Math.min(32, nCPU));
+ final int nWorkers = rnd.nextInt(minWorkers, maxWorkers + 1);
final int sizePerWorker = 1024;
final int maxSize = nWorkers * sizePerWorker;
- // The foreman keeps checking that the size of the arrays
- // obtained from the key and value sets is never less than the
- // previously observed size and is never greater than the maximum size
+ // The foreman busy-checks that the size of the arrays obtained
+ // from the keys and values views grows monotonically until it
+ // reaches the maximum size.
+
// NOTE: these size constraints are not specific to toArray and are
// applicable to any form of traversal of the collection views
CompletableFuture<?> foreman = CompletableFuture.runAsync(new Runnable() {
@@ -62,44 +65,37 @@
private boolean checkProgress(Object[] a) {
int size = a.length;
- if (size < prevSize) throw new RuntimeException("WRONG WAY");
- if (size > maxSize) throw new RuntimeException("OVERSHOOT");
- if (size == maxSize) return true;
+ if (size < prevSize || size > maxSize)
+ throw new AssertionError(
+ String.format("prevSize=%d size=%d maxSize=%d",
+ prevSize, size, maxSize));
prevSize = size;
- return false;
+ return size == maxSize;
}
- @Override
public void run() {
- try {
- Integer[] empty = new Integer[0];
- while (true) {
- if (checkProgress(m.values().toArray())) return;
- if (checkProgress(m.keySet().toArray())) return;
- if (checkProgress(m.values().toArray(empty))) return;
- if (checkProgress(m.keySet().toArray(empty))) return;
- }
- }
- catch (Throwable t) {
- throwable[0] = t;
- }
+ Integer[] empty = new Integer[0];
+ for (;;)
+ if (checkProgress(m.values().toArray())
+ & checkProgress(m.keySet().toArray())
+ & checkProgress(m.values().toArray(empty))
+ & checkProgress(m.keySet().toArray(empty)))
+ return;
}
});
- // Create workers
- // Each worker will put globally unique keys into the map
- CompletableFuture<?>[] workers = IntStream.range(0, nWorkers).
- mapToObj(w -> CompletableFuture.runAsync(() -> {
- for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++)
- m.put(o + i, i);
- })).
- toArray(CompletableFuture<?>[]::new);
+ // Each worker puts globally unique keys into the map
+ List<CompletableFuture<?>> workers =
+ IntStream.range(0, nWorkers)
+ .mapToObj(w -> (Runnable) () -> {
+ for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++)
+ m.put(o + i, i);
+ })
+ .map(CompletableFuture::runAsync)
+ .collect(Collectors.toList());
- // Wait for workers and then foreman to complete
- CompletableFuture.allOf(workers).join();
+ // Wait for workers and foreman to complete
+ workers.forEach(CompletableFuture<?>::join);
foreman.join();
-
- if (throwable[0] != null)
- throw throwable[0];
}
}
--- a/test/jdk/java/util/concurrent/ConcurrentQueues/OfferRemoveLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/ConcurrentQueues/OfferRemoveLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -25,7 +25,8 @@
* @test
* @bug 6316155 6595669 6871697 6868712
* @summary Test concurrent offer vs. remove
- * @run main OfferRemoveLoops 300
+ * @library /test/lib
+ * @run main OfferRemoveLoops 100
* @author Martin Buchholz
*/
@@ -43,10 +44,12 @@
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.Semaphore;
+import jdk.test.lib.Utils;
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
public class OfferRemoveLoops {
- final long testDurationMillisDefault = 10L * 1000L;
+ static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
+ final long testDurationMillisDefault = 10_000L;
final long testDurationMillis;
OfferRemoveLoops(String[] args) {
@@ -75,7 +78,6 @@
System.err.println(q.getClass().getSimpleName());
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
- final long timeoutMillis = 10L * 1000L;
final int maxChunkSize = 1042;
final int maxQueueSize = 10 * maxChunkSize;
final CountDownLatch done = new CountDownLatch(3);
@@ -156,7 +158,7 @@
done.countDown();
}};
- if (! done.await(timeoutMillis + testDurationMillis, MILLISECONDS)) {
+ if (! done.await(LONG_DELAY_MS + testDurationMillis, MILLISECONDS)) {
for (Thread thread : new Thread[] { offerer, remover, scanner }) {
if (thread.isAlive()) {
System.err.printf("Hung thread: %s%n", thread.getName());
--- a/test/jdk/java/util/concurrent/CountDownLatch/Basic.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/CountDownLatch/Basic.java Wed Sep 18 07:46:02 2019 +0200
@@ -23,70 +23,57 @@
/*
* @test
- * @bug 6332435
+ * @bug 6332435 8221168
* @summary Basic tests for CountDownLatch
* @library /test/lib
* @author Seetharam Avadhanam, Martin Buchholz
*/
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
import jdk.test.lib.Utils;
public class Basic {
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
- interface AwaiterFactory {
- Awaiter getAwaiter();
- }
-
abstract static class Awaiter extends Thread {
- private volatile Throwable result = null;
- protected void result(Throwable result) { this.result = result; }
- public Throwable result() { return this.result; }
- }
-
- private void toTheStartingGate(CountDownLatch gate) {
- try {
- gate.await();
- }
- catch (Throwable t) { fail(t); }
+ volatile Throwable exception;
+ volatile boolean interrupted;
+ abstract void realRun() throws Exception;
+ public final void run() {
+ try { realRun(); }
+ catch (Throwable ex) { exception = ex; }
+ interrupted = Thread.interrupted();
+ };
}
- private Awaiter awaiter(final CountDownLatch latch,
- final CountDownLatch gate) {
- return new Awaiter() { public void run() {
- System.out.println("without millis: " + latch.toString());
- gate.countDown();
-
- try {
+ static Awaiter awaiter(CountDownLatch latch,
+ CountDownLatch gate) {
+ return new Awaiter() {
+ public void realRun() throws InterruptedException {
+ gate.countDown();
latch.await();
- System.out.println("without millis - ComingOut");
- }
- catch (Throwable result) { result(result); }}};
+ }};
}
- private Awaiter awaiter(final CountDownLatch latch,
- final CountDownLatch gate,
- final long millis) {
- return new Awaiter() { public void run() {
- System.out.println("with millis: "+latch.toString());
- gate.countDown();
-
- try {
- latch.await(millis, TimeUnit.MILLISECONDS);
- System.out.println("with millis - ComingOut");
- }
- catch (Throwable result) { result(result); }}};
+ static Awaiter awaiter(CountDownLatch latch,
+ CountDownLatch gate,
+ long timeoutMillis) {
+ return new Awaiter() {
+ public void realRun() throws InterruptedException {
+ gate.countDown();
+ latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ }};
}
- AwaiterFactory awaiterFactory(CountDownLatch latch, CountDownLatch gate) {
- return () -> awaiter(latch, gate);
- }
-
- AwaiterFactory timedAwaiterFactory(CountDownLatch latch, CountDownLatch gate) {
- return () -> awaiter(latch, gate, LONG_DELAY_MS);
+ static Supplier<Awaiter> randomAwaiterSupplier(
+ CountDownLatch latch, CountDownLatch gate) {
+ return () -> (ThreadLocalRandom.current().nextBoolean())
+ ? awaiter(latch, gate)
+ : awaiter(latch, gate, LONG_DELAY_MS);
}
//----------------------------------------------------------------
@@ -94,28 +81,24 @@
//----------------------------------------------------------------
public static void normalUse() throws Throwable {
int count = 0;
- Basic test = new Basic();
CountDownLatch latch = new CountDownLatch(3);
Awaiter[] a = new Awaiter[12];
for (int i = 0; i < 3; i++) {
CountDownLatch gate = new CountDownLatch(4);
- AwaiterFactory factory1 = test.awaiterFactory(latch, gate);
- AwaiterFactory factory2 = test.timedAwaiterFactory(latch, gate);
- a[count] = factory1.getAwaiter(); a[count++].start();
- a[count] = factory1.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
- test.toTheStartingGate(gate);
- System.out.println("Main Thread: " + latch.toString());
+ Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ gate.await();
latch.countDown();
checkCount(latch, 2-i);
}
- for (int i = 0; i < 12; i++)
- a[i].join();
-
- for (int i = 0; i < 12; i++)
- checkResult(a[i], null);
+ for (Awaiter awaiter : a)
+ awaiter.join();
+ for (Awaiter awaiter : a)
+ checkException(awaiter, null);
}
//----------------------------------------------------------------
@@ -123,38 +106,38 @@
//----------------------------------------------------------------
public static void threadInterrupted() throws Throwable {
int count = 0;
- Basic test = new Basic();
CountDownLatch latch = new CountDownLatch(3);
Awaiter[] a = new Awaiter[12];
for (int i = 0; i < 3; i++) {
CountDownLatch gate = new CountDownLatch(4);
- AwaiterFactory factory1 = test.awaiterFactory(latch, gate);
- AwaiterFactory factory2 = test.timedAwaiterFactory(latch, gate);
- a[count] = factory1.getAwaiter(); a[count++].start();
- a[count] = factory1.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
+ Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ gate.await();
a[count-1].interrupt();
- test.toTheStartingGate(gate);
- System.out.println("Main Thread: " + latch.toString());
latch.countDown();
checkCount(latch, 2-i);
}
- for (int i = 0; i < 12; i++)
- a[i].join();
-
- for (int i = 0; i < 12; i++)
- checkResult(a[i],
- (i % 4) == 3 ? InterruptedException.class : null);
+ for (Awaiter awaiter : a)
+ awaiter.join();
+ for (int i = 0; i < a.length; i++) {
+ Awaiter awaiter = a[i];
+ Throwable ex = awaiter.exception;
+ if ((i % 4) == 3 && !awaiter.interrupted)
+ checkException(awaiter, InterruptedException.class);
+ else
+ checkException(awaiter, null);
+ }
}
//----------------------------------------------------------------
// One thread timed out
//----------------------------------------------------------------
public static void timeOut() throws Throwable {
- int count =0;
- Basic test = new Basic();
+ int count = 0;
CountDownLatch latch = new CountDownLatch(3);
Awaiter[] a = new Awaiter[12];
@@ -162,54 +145,56 @@
for (int i = 0; i < 3; i++) {
CountDownLatch gate = new CountDownLatch(4);
- AwaiterFactory factory1 = test.awaiterFactory(latch, gate);
- AwaiterFactory factory2 = test.timedAwaiterFactory(latch, gate);
- a[count] = test.awaiter(latch, gate, timeout[i]); a[count++].start();
- a[count] = factory1.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
- a[count] = factory2.getAwaiter(); a[count++].start();
- test.toTheStartingGate(gate);
- System.out.println("Main Thread: " + latch.toString());
+ Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
+ a[count] = awaiter(latch, gate, timeout[i]); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ a[count] = s.get(); a[count++].start();
+ gate.await();
latch.countDown();
checkCount(latch, 2-i);
}
- for (int i = 0; i < 12; i++)
- a[i].join();
-
- for (int i = 0; i < 12; i++)
- checkResult(a[i], null);
+ for (Awaiter awaiter : a)
+ awaiter.join();
+ for (Awaiter awaiter : a)
+ checkException(awaiter, null);
}
public static void main(String[] args) throws Throwable {
- normalUse();
- threadInterrupted();
- timeOut();
+ try {
+ normalUse();
+ } catch (Throwable ex) { fail(ex); }
+ try {
+ threadInterrupted();
+ } catch (Throwable ex) { fail(ex); }
+ try {
+ timeOut();
+ } catch (Throwable ex) { fail(ex); }
+
if (failures.get() > 0L)
throw new AssertionError(failures.get() + " failures");
}
- private static final AtomicInteger failures = new AtomicInteger(0);
+ static final AtomicInteger failures = new AtomicInteger(0);
- private static void fail(String msg) {
+ static void fail(String msg) {
fail(new AssertionError(msg));
}
- private static void fail(Throwable t) {
+ static void fail(Throwable t) {
t.printStackTrace();
failures.getAndIncrement();
}
- private static void checkCount(CountDownLatch b, int expected) {
+ static void checkCount(CountDownLatch b, int expected) {
if (b.getCount() != expected)
fail("Count = " + b.getCount() +
", expected = " + expected);
}
- private static void checkResult(Awaiter a, Class c) {
- Throwable t = a.result();
- if (! ((t == null && c == null) || c.isInstance(t))) {
- System.out.println("Mismatch: " + t + ", " + c.getName());
- failures.getAndIncrement();
- }
+ static void checkException(Awaiter awaiter, Class<? extends Throwable> c) {
+ Throwable ex = awaiter.exception;
+ if (! ((ex == null && c == null) || c.isInstance(ex)))
+ fail("Expected: " + c + ", got: " + ex);
}
}
--- a/test/jdk/java/util/concurrent/CyclicBarrier/Basic.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/CyclicBarrier/Basic.java Wed Sep 18 07:46:02 2019 +0200
@@ -37,6 +37,7 @@
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.test.lib.Utils;
@@ -293,37 +294,42 @@
* Handling of extra interrupts while waiting - tests for bug 6366811
*/
private static void testInterrupts() {
- final int N = 10;
+ final int N = ThreadLocalRandom.current().nextInt(2, 10);
final CyclicBarrier startingGate = new CyclicBarrier(N+1);
/**
* A version of Awaiter that also records interrupted state.
*/
class Waiter extends CheckedThread {
- private boolean timed;
- private CyclicBarrier barrier;
- private CountDownLatch doneSignal;
- private Throwable throwable;
- private boolean interrupted;
+ private final boolean timed;
+ private final CyclicBarrier barrier;
+ private final CountDownLatch doneSignal;
+ volatile Throwable throwable;
+ volatile boolean interruptStatusSetAfterAwait;
- public Waiter(boolean timed,
- CountDownLatch doneSignal,
- CyclicBarrier barrier) {
- this.timed = timed;
+ public Waiter(CountDownLatch doneSignal, CyclicBarrier barrier) {
+ this.timed = ThreadLocalRandom.current().nextBoolean();
this.doneSignal = doneSignal;
this.barrier = barrier;
}
- Throwable throwable() { return this.throwable; }
- boolean interruptBit() { return this.interrupted; }
+
void realRun() throws Throwable {
startingGate.await(LONG_DELAY_MS, MILLISECONDS);
+
try {
if (timed) barrier.await(LONG_DELAY_MS, MILLISECONDS);
- else barrier.await(); }
- catch (Throwable throwable) { this.throwable = throwable; }
+ else barrier.await();
+ } catch (Throwable throwable) {
+ this.throwable = throwable;
+ }
- try { doneSignal.await(LONG_DELAY_MS, MILLISECONDS); }
- catch (InterruptedException e) { interrupted = true; }
+ try {
+ check(doneSignal.await(LONG_DELAY_MS, MILLISECONDS));
+ if (Thread.interrupted())
+ interruptStatusSetAfterAwait = true;
+ } catch (InterruptedException e) {
+ interruptStatusSetAfterAwait = true;
+ }
}
}
@@ -352,7 +358,7 @@
} catch (Throwable t) { unexpected(t); }
}};
for (int i = 0; i < N; i++) {
- Waiter waiter = new Waiter(i < N/2, doneSignal, barrier);
+ Waiter waiter = new Waiter(doneSignal, barrier);
waiter.start();
waiters.add(waiter);
}
@@ -360,16 +366,14 @@
while (barrier.getNumberWaiting() < N) Thread.yield();
barrier.await();
doneSignal.countDown();
- int countInterrupted = 0;
- int countInterruptedException = 0;
- int countBrokenBarrierException = 0;
+ int countInterruptStatusSetAfterAwait = 0;
for (Waiter waiter : waiters) {
waiter.join();
- equal(waiter.throwable(), null);
- if (waiter.interruptBit())
- countInterrupted++;
+ equal(waiter.throwable, null);
+ if (waiter.interruptStatusSetAfterAwait)
+ countInterruptStatusSetAfterAwait++;
}
- equal(countInterrupted, N/2);
+ equal(countInterruptStatusSetAfterAwait, N/2);
check(! barrier.isBroken());
} catch (Throwable t) { unexpected(t); }
@@ -381,31 +385,33 @@
final CyclicBarrier barrier = new CyclicBarrier(N+1);
final List<Waiter> waiters = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
- Waiter waiter = new Waiter(i < N/2, doneSignal, barrier);
+ Waiter waiter = new Waiter(doneSignal, barrier);
waiter.start();
waiters.add(waiter);
}
startingGate.await(LONG_DELAY_MS, MILLISECONDS);
while (barrier.getNumberWaiting() < N) Thread.yield();
- for (int i = 0; i < N/2; i++)
- waiters.get(i).interrupt();
+ for (int i = 0; i < N/2; i++) {
+ Thread waiter = waiters.get(i);
+ waiter.interrupt();
+ }
doneSignal.countDown();
- int countInterrupted = 0;
int countInterruptedException = 0;
int countBrokenBarrierException = 0;
+ int countInterruptStatusSetAfterAwait = 0;
for (Waiter waiter : waiters) {
waiter.join();
- if (waiter.throwable() instanceof InterruptedException)
+ if (waiter.throwable instanceof InterruptedException)
countInterruptedException++;
- if (waiter.throwable() instanceof BrokenBarrierException)
+ if (waiter.throwable instanceof BrokenBarrierException)
countBrokenBarrierException++;
- if (waiter.interruptBit())
- countInterrupted++;
+ if (waiter.interruptStatusSetAfterAwait)
+ countInterruptStatusSetAfterAwait++;
}
- equal(countInterrupted, N/2-1);
equal(countInterruptedException, 1);
equal(countBrokenBarrierException, N-1);
checkBroken(barrier);
+ equal(countInterruptStatusSetAfterAwait, N/2-1);
reset(barrier);
} catch (Throwable t) { unexpected(t); }
}
--- a/test/jdk/java/util/concurrent/FutureTask/BlockingTaskExecutor.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/FutureTask/BlockingTaskExecutor.java Wed Sep 18 07:46:02 2019 +0200
@@ -103,7 +103,7 @@
*/
static class NotificationReceiver {
/** Has the notifiee been notified? */
- boolean notified = false;
+ boolean notified;
/**
* Notify the notification receiver.
--- a/test/jdk/java/util/concurrent/FutureTask/CancelledFutureLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/FutureTask/CancelledFutureLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -130,7 +130,7 @@
long endTime = System.nanoTime();
long time = endTime - timer.startTime;
if (print) {
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
}
--- a/test/jdk/java/util/concurrent/FutureTask/DoneTimedGetLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/FutureTask/DoneTimedGetLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -33,6 +33,7 @@
/*
* @test
+ * @library /test/lib
* @run main DoneTimedGetLoops 300
* @summary isDone returning true guarantees that subsequent timed get
* will never throw TimeoutException.
@@ -42,10 +43,12 @@
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import jdk.test.lib.Utils;
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
public class DoneTimedGetLoops {
- final long testDurationMillisDefault = 10L * 1000L;
+ static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
+ final long testDurationMillisDefault = 10_000L;
final long testDurationMillis;
static class PublicFutureTask extends FutureTask<Boolean> {
@@ -63,7 +66,6 @@
void test(String[] args) throws Throwable {
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
- final long timeoutMillis = 10L * 1000L;
final AtomicReference<PublicFutureTask> normalRef
= new AtomicReference<>();
@@ -136,13 +138,13 @@
setterException,
doneTimedGetNormal,
doneTimedGetAbnormal }) {
- thread.join(timeoutMillis + testDurationMillis);
+ thread.join(LONG_DELAY_MS + testDurationMillis);
if (thread.isAlive()) {
System.err.printf("Hung thread: %s%n", thread.getName());
failed++;
for (StackTraceElement e : thread.getStackTrace())
System.err.println(e);
- thread.join(timeoutMillis);
+ thread.join(LONG_DELAY_MS);
}
}
}
--- a/test/jdk/java/util/concurrent/Phaser/FickleRegister.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/Phaser/FickleRegister.java Wed Sep 18 07:46:02 2019 +0200
@@ -43,7 +43,7 @@
public class FickleRegister {
final AtomicLong count = new AtomicLong(0);
- final long testDurationMillisDefault = 10L * 1000L;
+ final long testDurationMillisDefault = 10_000L;
final long testDurationMillis;
final long quittingTimeNanos;
final int chunkSize = 1000;
--- a/test/jdk/java/util/concurrent/Phaser/TieredArriveLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/Phaser/TieredArriveLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -40,7 +40,7 @@
import java.util.concurrent.Phaser;
public class TieredArriveLoops {
- final long testDurationMillisDefault = 10L * 1000L;
+ final long testDurationMillisDefault = 10_000L;
final long testDurationMillis;
final long quittingTimeNanos;
--- a/test/jdk/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java Wed Sep 18 07:46:02 2019 +0200
@@ -86,7 +86,7 @@
if (q.remove(1000) != null)
break;
System.out.printf(
- "%d/%d unqueued references remaining%n", j, n);
+ "%d/%d unqueued references remaining%n", j + 1, n);
}
}
}
--- a/test/jdk/java/util/concurrent/TimeUnit/Basic.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/TimeUnit/Basic.java Wed Sep 18 07:46:02 2019 +0200
@@ -24,6 +24,7 @@
/* @test
* @bug 5057341 6363898
* @summary Basic tests for TimeUnit
+ * @library /test/lib
* @author Martin Buchholz
*/
@@ -39,12 +40,20 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import jdk.test.lib.Utils;
public class Basic {
+ static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
+
private static void realMain(String[] args) throws Throwable {
for (TimeUnit u : TimeUnit.values()) {
@@ -71,18 +80,33 @@
equal(1000L, MILLISECONDS.toMicros(1));
equal(1000L, MICROSECONDS.toNanos(1));
- long t0 = System.nanoTime();
- MILLISECONDS.sleep(3); /* See windows bug 6313903, might not sleep */
- long elapsedMillis = (System.nanoTime() - t0)/(1000L * 1000L);
- System.out.printf("elapsed=%d%n", elapsedMillis);
- check(elapsedMillis >= 0);
- /* Might not sleep on windows: check(elapsedMillis >= 3); */
- check(elapsedMillis < 1000);
+ //----------------------------------------------------------------
+ // TimeUnit.sleep sleeps for at least the specified time.
+ // TimeUnit.sleep(x, unit) for x <= 0 does not sleep at all.
+ //----------------------------------------------------------------
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ int maxTimeoutMillis = rnd.nextInt(1, 12);
+ List<CompletableFuture<?>> workers =
+ IntStream.range(-1, maxTimeoutMillis + 1)
+ .mapToObj(timeoutMillis -> (Runnable) () -> {
+ try {
+ long startTime = System.nanoTime();
+ MILLISECONDS.sleep(timeoutMillis);
+ long elapsedNanos = System.nanoTime() - startTime;
+ long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+ check(elapsedNanos >= timeoutNanos);
+ } catch (InterruptedException fail) {
+ throw new AssertionError(fail);
+ }})
+ .map(CompletableFuture::runAsync)
+ .collect(Collectors.toList());
+
+ workers.forEach(CompletableFuture<?>::join);
//----------------------------------------------------------------
// Tests for serialized form compatibility with previous release
//----------------------------------------------------------------
- byte[] serializedForm = /* Generated using tiger */
+ byte[] serializedForm = /* Generated using JDK 5 */
{-84, -19, 0, 5, '~', 'r', 0, 29, 'j', 'a', 'v', 'a', '.',
'u', 't', 'i', 'l', '.', 'c', 'o', 'n', 'c', 'u', 'r', 'r', 'e',
'n', 't', '.', 'T', 'i', 'm', 'e', 'U', 'n', 'i', 't', 0, 0,
--- a/test/jdk/java/util/concurrent/atomic/DoubleAdderDemo.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/atomic/DoubleAdderDemo.java Wed Sep 18 07:46:02 2019 +0200
@@ -103,8 +103,8 @@
long total = (long)nthreads * incs;
if (sum != (double)total)
throw new Error(sum + " != " + total);
- double secs = (double)time / (1000L * 1000 * 1000);
- long rate = total * (1000L) / time;
+ double secs = (double)time / 1000_000_000L;
+ long rate = total * 1000L / time;
System.out.printf("threads:%3d Time: %7.3fsec Incs per microsec: %4d\n",
nthreads, secs, rate);
}
--- a/test/jdk/java/util/concurrent/locks/Lock/CheckedLockLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/Lock/CheckedLockLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -136,7 +136,7 @@
long time = timer.getTime();
long tpi = time / (iters * nthreads);
System.out.print("\t" + LoopHelpers.rightJustify(tpi) + " ns per update");
- // double secs = (double)(time) / 1000000000.0;
+ // double secs = (double)time / 1000000000.0;
// System.out.print("\t " + secs + "s run time");
System.out.println();
--- a/test/jdk/java/util/concurrent/locks/Lock/FlakyMutex.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/Lock/FlakyMutex.java Wed Sep 18 07:46:02 2019 +0200
@@ -49,31 +49,51 @@
static class MyRuntimeException extends RuntimeException {}
static void checkThrowable(Throwable t) {
- check((t instanceof MyError) ||
+ if (!((t instanceof MyError) ||
(t instanceof MyException) ||
- (t instanceof MyRuntimeException));
+ (t instanceof MyRuntimeException)))
+ unexpected(t);
}
static void realMain(String[] args) throws Throwable {
- final int nThreads = 3;
+ final ThreadLocalRandom rndMain = ThreadLocalRandom.current();
+ final int nCpus = Runtime.getRuntime().availableProcessors();
+ final int maxThreads = Math.min(4, nCpus);
+ final int nThreads = rndMain.nextInt(1, maxThreads + 1);
final int iterations = 10_000;
final CyclicBarrier startingGate = new CyclicBarrier(nThreads);
+ final ExecutorService es = Executors.newFixedThreadPool(nThreads);
final FlakyMutex mutex = new FlakyMutex();
- final ExecutorService es = Executors.newFixedThreadPool(nThreads);
final Runnable task = () -> {
try {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
startingGate.await();
for (int i = 0; i < iterations; i++) {
for (;;) {
- try { mutex.lock(); break; }
- catch (Throwable t) { checkThrowable(t); }
+ try {
+ if (rnd.nextBoolean())
+ mutex.lock();
+ else
+ mutex.lockInterruptibly();
+ break;
+ } catch (Throwable t) { checkThrowable(t); }
}
- try { check(! mutex.tryLock()); }
- catch (Throwable t) { checkThrowable(t); }
+ if (rnd.nextBoolean()) {
+ try {
+ check(! mutex.tryLock());
+ } catch (Throwable t) { checkThrowable(t); }
+ }
- try { check(! mutex.tryLock(1, TimeUnit.MICROSECONDS)); }
- catch (Throwable t) { checkThrowable(t); }
+ if (rnd.nextInt(10) == 0) {
+ try {
+ check(! mutex.tryLock(1, TimeUnit.MICROSECONDS));
+ } catch (Throwable t) { checkThrowable(t); }
+ }
+
+ if (rnd.nextBoolean()) {
+ check(mutex.isLocked());
+ }
mutex.unlock();
}
@@ -146,7 +166,11 @@
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
- try {realMain(args);} catch (Throwable t) {unexpected(t);}
+ int runsPerTest = Integer.getInteger("jsr166.runsPerTest", 1);
+ try {
+ for (int i = runsPerTest; i--> 0; )
+ realMain(args);
+ } catch (Throwable t) { unexpected(t); }
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
@SuppressWarnings("unchecked")
--- a/test/jdk/java/util/concurrent/locks/Lock/TimedAcquireLeak.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/Lock/TimedAcquireLeak.java Wed Sep 18 07:46:02 2019 +0200
@@ -42,6 +42,8 @@
import java.io.Reader;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
@@ -72,9 +74,9 @@
return new File(bin, programName).getPath();
}
- static final String java = javaProgramPath("java");
- static final String jmap = javaProgramPath("jmap");
- static final String jps = javaProgramPath("jps");
+ static final String javaPath = javaProgramPath("java");
+ static final String jmapPath = javaProgramPath("jmap");
+ static final String jpsPath = javaProgramPath("jps");
static String outputOf(Reader r) throws IOException {
final StringBuilder sb = new StringBuilder();
@@ -159,7 +161,11 @@
static String match(String s, String regex, int group) {
Matcher matcher = Pattern.compile(regex).matcher(s);
- matcher.find();
+ if (! matcher.find()) {
+ String msg = String.format(
+ "match failed: s=%s regex=%s", s, regex);
+ throw new AssertionError(msg);
+ }
return matcher.group(group);
}
@@ -171,21 +177,20 @@
static int objectsInUse(final Process child,
final String childPid,
- final String className) {
- final String regex =
- "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +\\Q"+className+"\\E(?:$| )";
- final Callable<Integer> objectsInUse =
- new Callable<Integer>() { public Integer call() {
- Integer i = Integer.parseInt(
- match(commandOutputOf(jmap, "-histo:live", childPid),
- regex, 1));
- if (i > 100)
- System.out.print(
- commandOutputOf(jmap,
- "-dump:file=dump,format=b",
- childPid));
- return i;
- }};
+ final String classNameRegex) {
+ String regex =
+ "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +"+classNameRegex+"(?:$| )";
+ Callable<Integer> objectsInUse = () -> {
+ int i = Integer.parseInt(
+ match(commandOutputOf(jmapPath, "-histo:live", childPid),
+ regex, 1));
+ if (i > 100)
+ System.out.print(
+ commandOutputOf(jmapPath,
+ "-dump:file=dump,format=b",
+ childPid));
+ return i;
+ };
try { return rendezvousParent(child, objectsInUse); }
catch (Throwable t) { unexpected(t); return -1; }
}
@@ -196,26 +201,27 @@
return;
final String childClassName = Job.class.getName();
- final String classToCheckForLeaks = Job.classToCheckForLeaks();
- final String uniqueID =
- String.valueOf(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
+ final String classNameRegex = Job.classNameRegexToCheckForLeaks();
+ final String uniqueID = String.valueOf(
+ ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
- final String[] jobCmd = {
- java, "-Xmx8m", "-XX:+UsePerfData",
- "-classpath", System.getProperty("test.class.path"),
- childClassName, uniqueID
- };
+ final ArrayList<String> jobCmd = new ArrayList<>();
+ Collections.addAll(
+ jobCmd, javaPath, "-Xmx8m", "-XX:+UsePerfData",
+ "-classpath", System.getProperty("test.class.path"));
+ Collections.addAll(jobCmd, Utils.getTestJavaOpts());
+ Collections.addAll(jobCmd, childClassName, uniqueID);
final Process p = new ProcessBuilder(jobCmd).start();
// Ensure subprocess jvm has started, so that jps can find it
p.getInputStream().read();
sendByte(p.getOutputStream());
final String childPid =
- match(commandOutputOf(jps, "-m"),
+ match(commandOutputOf(jpsPath, "-m"),
"(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
- final int n0 = objectsInUse(p, childPid, classToCheckForLeaks);
- final int n1 = objectsInUse(p, childPid, classToCheckForLeaks);
+ final int n0 = objectsInUse(p, childPid, classNameRegex);
+ final int n1 = objectsInUse(p, childPid, classNameRegex);
equal(p.waitFor(), 0);
equal(p.exitValue(), 0);
failed += p.exitValue();
@@ -226,7 +232,7 @@
// implementation, and needing occasional adjustment.
System.out.printf("%d -> %d%n", n0, n1);
// Almost always n0 == n1
- // Maximum jitter observed in practice is 10 -> 17
+ // Maximum jitter observed in practice is 7
check(Math.abs(n1 - n0) < 10);
check(n1 < 25);
drainers.shutdown();
@@ -244,9 +250,9 @@
// - in between calls to rendezvousChild, run code that may leak.
//----------------------------------------------------------------
public static class Job {
- static String classToCheckForLeaks() {
+ static String classNameRegexToCheckForLeaks() {
return
- "java.util.concurrent.locks.AbstractQueuedSynchronizer$Node";
+ "\\Qjava.util.concurrent.locks.AbstractQueuedSynchronizer$\\E[A-Za-z]+";
}
public static void main(String[] args) throws Throwable {
--- a/test/jdk/java/util/concurrent/locks/ReentrantLock/CancelledLockLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/ReentrantLock/CancelledLockLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -93,7 +93,7 @@
barrier.await();
if (print) {
long time = timer.getTime();
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
}
--- a/test/jdk/java/util/concurrent/locks/ReentrantLock/LockOncePerThreadLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/ReentrantLock/LockOncePerThreadLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -94,7 +94,7 @@
barrier.await();
if (print) {
long time = timer.getTime();
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
}
--- a/test/jdk/java/util/concurrent/locks/ReentrantLock/SimpleReentrantLockLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/ReentrantLock/SimpleReentrantLockLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -95,7 +95,7 @@
long time = timer.getTime();
long tpi = time / ((long)iters * nthreads);
System.out.print("\t" + LoopHelpers.rightJustify(tpi) + " ns per lock");
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
}
--- a/test/jdk/java/util/concurrent/locks/ReentrantLock/TimeoutLockLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/ReentrantLock/TimeoutLockLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -96,7 +96,7 @@
barrier.await();
if (print) {
long time = timer.getTime();
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
}
--- a/test/jdk/java/util/concurrent/locks/ReentrantReadWriteLock/MapLoops.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/locks/ReentrantReadWriteLock/MapLoops.java Wed Sep 18 07:46:02 2019 +0200
@@ -91,8 +91,8 @@
premove = Integer.parseInt(args[4]);
// normalize probabilities wrt random number generator
- removesPerMaxRandom = (int)(((double)premove/100.0 * 0x7FFFFFFFL));
- insertsPerMaxRandom = (int)(((double)pinsert/100.0 * 0x7FFFFFFFL));
+ removesPerMaxRandom = (int)((double)premove/100.0 * 0x7FFFFFFFL);
+ insertsPerMaxRandom = (int)((double)pinsert/100.0 * 0x7FFFFFFFL);
System.out.println("Using " + mapClass.getName());
@@ -125,7 +125,7 @@
long time = timer.getTime();
long tpo = time / (i * (long)nops);
System.out.print(LoopHelpers.rightJustify(tpo) + " ns per op");
- double secs = (double)(time) / 1000000000.0;
+ double secs = (double)time / 1000000000.0;
System.out.println("\t " + secs + "s run time");
map.clear();
}
--- a/test/jdk/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -39,7 +39,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
-import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject;
@@ -1286,19 +1286,31 @@
/**
* Tests scenario for
* JDK-8191937: Lost interrupt in AbstractQueuedSynchronizer when tryAcquire methods throw
+ * ant -Djsr166.tckTestClass=AbstractQueuedLongSynchronizerTest -Djsr166.methodFilter=testInterruptedFailingAcquire -Djsr166.runsPerTest=10000 tck
*/
- public void testInterruptedFailingAcquire() throws InterruptedException {
- final RuntimeException ex = new RuntimeException();
+ public void testInterruptedFailingAcquire() throws Throwable {
+ class PleaseThrow extends RuntimeException {}
+ final PleaseThrow ex = new PleaseThrow();
+ final AtomicBoolean thrown = new AtomicBoolean();
// A synchronizer only offering a choice of failure modes
class Sync extends AbstractQueuedLongSynchronizer {
- boolean pleaseThrow;
+ volatile boolean pleaseThrow;
+ void maybeThrow() {
+ if (pleaseThrow) {
+ // assert: tryAcquire methods can throw at most once
+ if (! thrown.compareAndSet(false, true))
+ throw new AssertionError();
+ throw ex;
+ }
+ }
+
@Override protected boolean tryAcquire(long ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return false;
}
@Override protected long tryAcquireShared(long ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return -1;
}
@Override protected boolean tryRelease(long ignored) {
@@ -1310,30 +1322,87 @@
}
final Sync s = new Sync();
+ final boolean acquireInterruptibly = randomBoolean();
+ final Action[] uninterruptibleAcquireActions = {
+ () -> s.acquire(1),
+ () -> s.acquireShared(1),
+ };
+ final long nanosTimeout = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+ final Action[] interruptibleAcquireActions = {
+ () -> s.acquireInterruptibly(1),
+ () -> s.acquireSharedInterruptibly(1),
+ () -> s.tryAcquireNanos(1, nanosTimeout),
+ () -> s.tryAcquireSharedNanos(1, nanosTimeout),
+ };
+ final Action[] releaseActions = {
+ () -> s.release(1),
+ () -> s.releaseShared(1),
+ };
+ final Action acquireAction = acquireInterruptibly
+ ? chooseRandomly(interruptibleAcquireActions)
+ : chooseRandomly(uninterruptibleAcquireActions);
+ final Action releaseAction
+ = chooseRandomly(releaseActions);
+ // From os_posix.cpp:
+ //
+ // NOTE that since there is no "lock" around the interrupt and
+ // is_interrupted operations, there is the possibility that the
+ // interrupted flag (in osThread) will be "false" but that the
+ // low-level events will be in the signaled state. This is
+ // intentional. The effect of this is that Object.wait() and
+ // LockSupport.park() will appear to have a spurious wakeup, which
+ // is allowed and not harmful, and the possibility is so rare that
+ // it is not worth the added complexity to add yet another lock.
final Thread thread = newStartedThread(new CheckedRunnable() {
- public void realRun() {
+ public void realRun() throws Throwable {
try {
- if (ThreadLocalRandom.current().nextBoolean())
- s.acquire(1);
- else
- s.acquireShared(1);
+ acquireAction.run();
shouldThrow();
- } catch (Throwable t) {
- assertSame(ex, t);
- assertTrue(Thread.interrupted());
+ } catch (InterruptedException possible) {
+ assertTrue(acquireInterruptibly);
+ assertFalse(Thread.interrupted());
+ } catch (PleaseThrow possible) {
+ awaitInterrupted();
}
}});
- waitForThreadToEnterWaitState(thread);
- assertSame(thread, s.getFirstQueuedThread());
- assertTrue(s.hasQueuedPredecessors());
- assertTrue(s.hasQueuedThreads());
- assertEquals(1, s.getQueueLength());
+ for (long startTime = 0L;; ) {
+ waitForThreadToEnterWaitState(thread);
+ if (s.getFirstQueuedThread() == thread
+ && s.hasQueuedPredecessors()
+ && s.hasQueuedThreads()
+ && s.getQueueLength() == 1
+ && s.hasContended())
+ break;
+ if (startTime == 0L)
+ startTime = System.nanoTime();
+ else if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+ fail("timed out waiting for AQS state: "
+ + "thread state=" + thread.getState()
+ + ", queued threads=" + s.getQueuedThreads());
+ Thread.yield();
+ }
s.pleaseThrow = true;
- thread.interrupt();
- s.release(1);
+ // release and interrupt, in random order
+ if (randomBoolean()) {
+ thread.interrupt();
+ releaseAction.run();
+ } else {
+ releaseAction.run();
+ thread.interrupt();
+ }
awaitTermination(thread);
+
+ if (! acquireInterruptibly)
+ assertTrue(thrown.get());
+
+ assertNull(s.getFirstQueuedThread());
+ assertFalse(s.hasQueuedPredecessors());
+ assertFalse(s.hasQueuedThreads());
+ assertEquals(0, s.getQueueLength());
+ assertTrue(s.getQueuedThreads().isEmpty());
+ assertTrue(s.hasContended());
}
}
--- a/test/jdk/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -40,7 +40,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
-import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
@@ -1337,19 +1337,31 @@
/**
* Tests scenario for
* JDK-8191937: Lost interrupt in AbstractQueuedSynchronizer when tryAcquire methods throw
+ * ant -Djsr166.tckTestClass=AbstractQueuedSynchronizerTest -Djsr166.methodFilter=testInterruptedFailingAcquire -Djsr166.runsPerTest=10000 tck
*/
- public void testInterruptedFailingAcquire() throws InterruptedException {
- final RuntimeException ex = new RuntimeException();
+ public void testInterruptedFailingAcquire() throws Throwable {
+ class PleaseThrow extends RuntimeException {}
+ final PleaseThrow ex = new PleaseThrow();
+ final AtomicBoolean thrown = new AtomicBoolean();
// A synchronizer only offering a choice of failure modes
class Sync extends AbstractQueuedSynchronizer {
- boolean pleaseThrow;
+ volatile boolean pleaseThrow;
+ void maybeThrow() {
+ if (pleaseThrow) {
+ // assert: tryAcquire methods can throw at most once
+ if (! thrown.compareAndSet(false, true))
+ throw new AssertionError();
+ throw ex;
+ }
+ }
+
@Override protected boolean tryAcquire(int ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return false;
}
@Override protected int tryAcquireShared(int ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return -1;
}
@Override protected boolean tryRelease(int ignored) {
@@ -1361,30 +1373,87 @@
}
final Sync s = new Sync();
+ final boolean acquireInterruptibly = randomBoolean();
+ final Action[] uninterruptibleAcquireActions = {
+ () -> s.acquire(1),
+ () -> s.acquireShared(1),
+ };
+ final long nanosTimeout = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+ final Action[] interruptibleAcquireActions = {
+ () -> s.acquireInterruptibly(1),
+ () -> s.acquireSharedInterruptibly(1),
+ () -> s.tryAcquireNanos(1, nanosTimeout),
+ () -> s.tryAcquireSharedNanos(1, nanosTimeout),
+ };
+ final Action[] releaseActions = {
+ () -> s.release(1),
+ () -> s.releaseShared(1),
+ };
+ final Action acquireAction = acquireInterruptibly
+ ? chooseRandomly(interruptibleAcquireActions)
+ : chooseRandomly(uninterruptibleAcquireActions);
+ final Action releaseAction
+ = chooseRandomly(releaseActions);
+ // From os_posix.cpp:
+ //
+ // NOTE that since there is no "lock" around the interrupt and
+ // is_interrupted operations, there is the possibility that the
+ // interrupted flag (in osThread) will be "false" but that the
+ // low-level events will be in the signaled state. This is
+ // intentional. The effect of this is that Object.wait() and
+ // LockSupport.park() will appear to have a spurious wakeup, which
+ // is allowed and not harmful, and the possibility is so rare that
+ // it is not worth the added complexity to add yet another lock.
final Thread thread = newStartedThread(new CheckedRunnable() {
- public void realRun() {
+ public void realRun() throws Throwable {
try {
- if (ThreadLocalRandom.current().nextBoolean())
- s.acquire(1);
- else
- s.acquireShared(1);
+ acquireAction.run();
shouldThrow();
- } catch (Throwable t) {
- assertSame(ex, t);
- assertTrue(Thread.interrupted());
+ } catch (InterruptedException possible) {
+ assertTrue(acquireInterruptibly);
+ assertFalse(Thread.interrupted());
+ } catch (PleaseThrow possible) {
+ awaitInterrupted();
}
}});
- waitForThreadToEnterWaitState(thread);
- assertSame(thread, s.getFirstQueuedThread());
- assertTrue(s.hasQueuedPredecessors());
- assertTrue(s.hasQueuedThreads());
- assertEquals(1, s.getQueueLength());
+ for (long startTime = 0L;; ) {
+ waitForThreadToEnterWaitState(thread);
+ if (s.getFirstQueuedThread() == thread
+ && s.hasQueuedPredecessors()
+ && s.hasQueuedThreads()
+ && s.getQueueLength() == 1
+ && s.hasContended())
+ break;
+ if (startTime == 0L)
+ startTime = System.nanoTime();
+ else if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+ fail("timed out waiting for AQS state: "
+ + "thread state=" + thread.getState()
+ + ", queued threads=" + s.getQueuedThreads());
+ Thread.yield();
+ }
s.pleaseThrow = true;
- thread.interrupt();
- s.release(1);
+ // release and interrupt, in random order
+ if (randomBoolean()) {
+ thread.interrupt();
+ releaseAction.run();
+ } else {
+ releaseAction.run();
+ thread.interrupt();
+ }
awaitTermination(thread);
+
+ if (! acquireInterruptibly)
+ assertTrue(thrown.get());
+
+ assertNull(s.getFirstQueuedThread());
+ assertFalse(s.hasQueuedPredecessors());
+ assertFalse(s.hasQueuedThreads());
+ assertEquals(0, s.getQueueLength());
+ assertTrue(s.getQueuedThreads().isEmpty());
+ assertTrue(s.hasContended());
}
}
--- a/test/jdk/java/util/concurrent/tck/ArrayBlockingQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ArrayBlockingQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -61,7 +61,7 @@
class Implementation implements CollectionImplementation {
public Class<?> klazz() { return ArrayBlockingQueue.class; }
public Collection emptyCollection() {
- boolean fair = ThreadLocalRandom.current().nextBoolean();
+ boolean fair = randomBoolean();
return populatedQueue(0, SIZE, 2 * SIZE, fair);
}
public Object makeElement(int i) { return i; }
@@ -367,7 +367,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(SIZE, q.size());
@@ -409,7 +409,7 @@
assertEquals(0, q.take());
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -431,21 +431,21 @@
Thread.currentThread().interrupt();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -486,7 +486,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -539,29 +539,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
--- a/test/jdk/java/util/concurrent/tck/BlockingQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/BlockingQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -253,7 +253,7 @@
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
@@ -274,7 +274,7 @@
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
barrier.await();
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -296,7 +296,7 @@
}});
await(threadStarted);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -325,19 +325,19 @@
*/
public void testTimedPollFromEmptyBlocksInterruptibly() {
final BlockingQueue q = emptyCollection();
- final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
- threadStarted.countDown();
+ pleaseInterrupt.countDown();
try {
- q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
- await(threadStarted);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ await(pleaseInterrupt);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -352,7 +352,7 @@
public void realRun() {
Thread.currentThread().interrupt();
try {
- q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
--- a/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -940,7 +940,7 @@
}
void runAsync(Runnable r1, Runnable r2) {
- boolean b = ThreadLocalRandom.current().nextBoolean();
+ boolean b = randomBoolean();
CompletableFuture<Void> f1 = CompletableFuture.runAsync(b ? r1 : r2);
CompletableFuture<Void> f2 = CompletableFuture.runAsync(b ? r2 : r1);
f1.join();
@@ -1003,10 +1003,6 @@
}
}
- <T> T chooseRandomly(T... choices) {
- return choices[ThreadLocalRandom.current().nextInt(choices.length)];
- }
-
/**
* Non-traversing Deque operations (that return null) are linearizable.
* Don't return null when the deque is observably never empty.
--- a/test/jdk/java/util/concurrent/tck/CountDownLatchTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/CountDownLatchTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -36,6 +36,7 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -99,7 +100,7 @@
assertEquals(2, l.getCount());
l.countDown();
assertEquals(1, l.getCount());
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
l.countDown();
assertEquals(0, l.getCount());
awaitTermination(t);
@@ -124,7 +125,7 @@
assertEquals(2, l.getCount());
l.countDown();
assertEquals(1, l.getCount());
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
l.countDown();
assertEquals(0, l.getCount());
awaitTermination(t);
@@ -156,7 +157,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -165,29 +166,30 @@
* timed await throws InterruptedException if interrupted before counted down
*/
public void testTimedAwait_Interruptible() {
- final CountDownLatch l = new CountDownLatch(1);
+ final int initialCount = ThreadLocalRandom.current().nextInt(1, 3);
+ final CountDownLatch l = new CountDownLatch(initialCount);
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
Thread.currentThread().interrupt();
try {
- l.await(LONG_DELAY_MS, MILLISECONDS);
+ l.await(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- l.await(LONG_DELAY_MS, MILLISECONDS);
+ l.await(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
- assertEquals(1, l.getCount());
+ assertEquals(initialCount, l.getCount());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -200,7 +202,11 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertEquals(1, l.getCount());
+
+ long startTime = System.nanoTime();
assertFalse(l.await(timeoutMillis(), MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
assertEquals(1, l.getCount());
}});
--- a/test/jdk/java/util/concurrent/tck/CyclicBarrierTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/CyclicBarrierTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -42,7 +42,6 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.Test;
@@ -323,34 +322,6 @@
}
/**
- * All threads block while a barrier is broken.
- */
- public void testReset_Leakage() throws InterruptedException {
- final CyclicBarrier c = new CyclicBarrier(2);
- final AtomicBoolean done = new AtomicBoolean();
- Thread t = newStartedThread(new CheckedRunnable() {
- public void realRun() {
- while (!done.get()) {
- try {
- while (c.isBroken())
- c.reset();
-
- c.await();
- shouldThrow();
- }
- catch (BrokenBarrierException | InterruptedException ok) {}
- }}});
-
- for (int i = 0; i < 4; i++) {
- delay(timeoutMillis());
- t.interrupt();
- }
- done.set(true);
- t.interrupt();
- awaitTermination(t);
- }
-
- /**
* Reset of a non-broken barrier does not break barrier
*/
public void testResetWithoutBreakage() throws Exception {
@@ -505,7 +476,7 @@
final ExecutorService e = Executors.newFixedThreadPool(nTasks);
final Runnable awaiter = () -> {
try {
- if (ThreadLocalRandom.current().nextBoolean())
+ if (randomBoolean())
barrier.await();
else
barrier.await(LONG_DELAY_MS, MILLISECONDS);
--- a/test/jdk/java/util/concurrent/tck/DelayQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/DelayQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -332,7 +332,7 @@
}
/**
- * timed offer does not time out
+ * Queue is unbounded, so timed offer never times out
*/
public void testTimedOffer() throws InterruptedException {
final DelayQueue q = new DelayQueue();
@@ -384,7 +384,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -436,30 +436,27 @@
final DelayQueue q = populatedQueue(SIZE);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(new PDelay(i),
((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS)));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
--- a/test/jdk/java/util/concurrent/tck/DoubleAccumulatorTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/DoubleAccumulatorTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -156,7 +156,7 @@
= new DoubleAccumulator((x, y) -> x + y, 0.0);
final int nThreads = ThreadLocalRandom.current().nextInt(1, 5);
final Phaser phaser = new Phaser(nThreads + 1);
- final int incs = 1_000_000;
+ final int incs = expensiveTests ? 1_000_000 : 100_000;
final double total = nThreads * incs/2.0 * (incs - 1); // Gauss
final Runnable task = () -> {
phaser.arriveAndAwaitAdvance();
--- a/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java Wed Sep 18 07:46:02 2019 +0200
@@ -38,7 +38,6 @@
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
-import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import junit.framework.Test;
@@ -81,7 +80,7 @@
Thread currentThread = Thread.currentThread();
Stream.of(systemClassLoader, null).forEach(cl -> {
- if (ThreadLocalRandom.current().nextBoolean())
+ if (randomBoolean())
// should always be permitted, without effect
currentThread.setContextClassLoader(cl);
});
--- a/test/jdk/java/util/concurrent/tck/ForkJoinTask8Test.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ForkJoinTask8Test.java Wed Sep 18 07:46:02 2019 +0200
@@ -559,6 +559,8 @@
AsyncFib f = new AsyncFib(8);
assertSame(f, f.fork());
helpQuiesce();
+ while (!f.isDone()) // wait out race
+ ;
assertEquals(0, getQueuedTaskCount());
f.checkCompletedNormally();
}};
--- a/test/jdk/java/util/concurrent/tck/ForkJoinTaskTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ForkJoinTaskTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -526,6 +526,8 @@
AsyncFib f = new AsyncFib(8);
assertSame(f, f.fork());
helpQuiesce();
+ while (!f.isDone()) // wait out race
+ ;
assertEquals(21, f.number);
assertEquals(0, getQueuedTaskCount());
checkCompletedNormally(f);
--- a/test/jdk/java/util/concurrent/tck/FutureTaskTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/FutureTaskTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -747,7 +747,7 @@
/**
* get is interruptible
*/
- public void testGet_interruptible() {
+ public void testGet_Interruptible() {
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
final FutureTask task = new FutureTask(new NoOpCallable());
Thread t = newStartedThread(new CheckedRunnable() {
@@ -776,27 +776,28 @@
/**
* timed get is interruptible
*/
- public void testTimedGet_interruptible() {
+ public void testTimedGet_Interruptible() {
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
final FutureTask task = new FutureTask(new NoOpCallable());
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws Exception {
Thread.currentThread().interrupt();
try {
- task.get(2*LONG_DELAY_MS, MILLISECONDS);
+ task.get(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- task.get(2*LONG_DELAY_MS, MILLISECONDS);
+ task.get(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkNotDone(task);
--- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java Wed Sep 18 07:46:02 2019 +0200
@@ -311,12 +311,13 @@
static volatile TestCase currentTestCase;
// static volatile int currentRun = 0;
static {
- Runnable checkForWedgedTest = new Runnable() { public void run() {
+ Runnable wedgedTestDetector = new Runnable() { public void run() {
// Avoid spurious reports with enormous runsPerTest.
// A single test case run should never take more than 1 second.
// But let's cap it at the high end too ...
- final int timeoutMinutes =
- Math.min(15, Math.max(runsPerTest / 60, 1));
+ final int timeoutMinutesMin = Math.max(runsPerTest / 60, 1)
+ * Math.max((int) delayFactor, 1);
+ final int timeoutMinutes = Math.min(15, timeoutMinutesMin);
for (TestCase lastTestCase = currentTestCase;;) {
try { MINUTES.sleep(timeoutMinutes); }
catch (InterruptedException unexpected) { break; }
@@ -336,7 +337,7 @@
}
lastTestCase = currentTestCase;
}}};
- Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest");
+ Thread thread = new Thread(wedgedTestDetector, "WedgedTestDetector");
thread.setDaemon(true);
thread.start();
}
@@ -380,7 +381,7 @@
// Never report first run of any test; treat it as a
// warmup run, notably to trigger all needed classloading,
if (i > 0)
- System.out.printf("%n%s: %d%n", toString(), elapsedMillis);
+ System.out.printf("%s: %d%n", toString(), elapsedMillis);
}
}
@@ -683,6 +684,12 @@
public static long MEDIUM_DELAY_MS;
public static long LONG_DELAY_MS;
+ /**
+ * A delay significantly longer than LONG_DELAY_MS.
+ * Use this in a thread that is waited for via awaitTermination(Thread).
+ */
+ public static long LONGER_DELAY_MS;
+
private static final long RANDOM_TIMEOUT;
private static final long RANDOM_EXPIRED_TIMEOUT;
private static final TimeUnit RANDOM_TIMEUNIT;
@@ -711,6 +718,20 @@
static TimeUnit randomTimeUnit() { return RANDOM_TIMEUNIT; }
/**
+ * Returns a random boolean; a "coin flip".
+ */
+ static boolean randomBoolean() {
+ return ThreadLocalRandom.current().nextBoolean();
+ }
+
+ /**
+ * Returns a random element from given choices.
+ */
+ <T> T chooseRandomly(T... choices) {
+ return choices[ThreadLocalRandom.current().nextInt(choices.length)];
+ }
+
+ /**
* Returns the shortest timed delay. This can be scaled up for
* slow machines using the jsr166.delay.factor system property,
* or via jtreg's -timeoutFactor: flag.
@@ -728,6 +749,7 @@
SMALL_DELAY_MS = SHORT_DELAY_MS * 5;
MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10;
LONG_DELAY_MS = SHORT_DELAY_MS * 200;
+ LONGER_DELAY_MS = 2 * LONG_DELAY_MS;
}
private static final long TIMEOUT_DELAY_MS
@@ -766,8 +788,8 @@
*/
public void threadRecordFailure(Throwable t) {
System.err.println(t);
- dumpTestThreads();
- threadFailure.compareAndSet(null, t);
+ if (threadFailure.compareAndSet(null, t))
+ dumpTestThreads();
}
public void setUp() {
@@ -1088,6 +1110,39 @@
}
}
+ /** Returns true if thread info might be useful in a thread dump. */
+ static boolean threadOfInterest(ThreadInfo info) {
+ final String name = info.getThreadName();
+ String lockName;
+ if (name == null)
+ return true;
+ if (name.equals("Signal Dispatcher")
+ || name.equals("WedgedTestDetector"))
+ return false;
+ if (name.equals("Reference Handler")) {
+ // Reference Handler stacktrace changed in JDK-8156500
+ StackTraceElement[] stackTrace; String methodName;
+ if ((stackTrace = info.getStackTrace()) != null
+ && stackTrace.length > 0
+ && (methodName = stackTrace[0].getMethodName()) != null
+ && methodName.equals("waitForReferencePendingList"))
+ return false;
+ // jdk8 Reference Handler stacktrace
+ if ((lockName = info.getLockName()) != null
+ && lockName.startsWith("java.lang.ref"))
+ return false;
+ }
+ if ((name.equals("Finalizer") || name.equals("Common-Cleaner"))
+ && (lockName = info.getLockName()) != null
+ && lockName.startsWith("java.lang.ref"))
+ return false;
+ if (name.startsWith("ForkJoinPool.commonPool-worker")
+ && (lockName = info.getLockName()) != null
+ && lockName.startsWith("java.util.concurrent.ForkJoinPool"))
+ return false;
+ return true;
+ }
+
/**
* A debugging tool to print stack traces of most threads, as jstack does.
* Uninteresting threads are filtered out.
@@ -1104,23 +1159,9 @@
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
System.err.println("------ stacktrace dump start ------");
- for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
- final String name = info.getThreadName();
- String lockName;
- if ("Signal Dispatcher".equals(name))
- continue;
- if ("Reference Handler".equals(name)
- && (lockName = info.getLockName()) != null
- && lockName.startsWith("java.lang.ref.Reference$Lock"))
- continue;
- if ("Finalizer".equals(name)
- && (lockName = info.getLockName()) != null
- && lockName.startsWith("java.lang.ref.ReferenceQueue$Lock"))
- continue;
- if ("checkForWedgedTest".equals(name))
- continue;
- System.err.print(info);
- }
+ for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true))
+ if (threadOfInterest(info))
+ System.err.print(info);
System.err.println("------ stacktrace dump end ------");
if (sm != null) System.setSecurityManager(sm);
@@ -1393,6 +1434,20 @@
}
/**
+ * Spin-waits up to LONG_DELAY_MS milliseconds for the current thread to
+ * be interrupted. Clears the interrupt status before returning.
+ */
+ void awaitInterrupted() {
+ for (long startTime = 0L; !Thread.interrupted(); ) {
+ if (startTime == 0L)
+ startTime = System.nanoTime();
+ else if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+ fail("timed out waiting for thread interrupt");
+ Thread.yield();
+ }
+ }
+
+ /**
* Returns the number of milliseconds since time given by
* startNanoTime, which must have been previously returned from a
* call to {@link System#nanoTime()}.
@@ -1401,19 +1456,6 @@
return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
}
-// void assertTerminatesPromptly(long timeoutMillis, Runnable r) {
-// long startTime = System.nanoTime();
-// try {
-// r.run();
-// } catch (Throwable fail) { threadUnexpectedException(fail); }
-// if (millisElapsedSince(startTime) > timeoutMillis/2)
-// throw new AssertionError("did not return promptly");
-// }
-
-// void assertTerminatesPromptly(Runnable r) {
-// assertTerminatesPromptly(LONG_DELAY_MS/2, r);
-// }
-
/**
* Checks that timed f.get() returns the expected value, and does not
* wait for the timeout to elapse before returning.
@@ -1448,15 +1490,21 @@
* to terminate (using {@link Thread#join(long)}), else interrupts
* the thread (in the hope that it may terminate later) and fails.
*/
- void awaitTermination(Thread t, long timeoutMillis) {
+ void awaitTermination(Thread thread, long timeoutMillis) {
try {
- t.join(timeoutMillis);
+ thread.join(timeoutMillis);
} catch (InterruptedException fail) {
threadUnexpectedException(fail);
- } finally {
- if (t.getState() != Thread.State.TERMINATED) {
- t.interrupt();
- threadFail("timed out waiting for thread to terminate");
+ }
+ if (thread.getState() != Thread.State.TERMINATED) {
+ String detail = String.format(
+ "timed out waiting for thread to terminate, thread=%s, state=%s" ,
+ thread, thread.getState());
+ try {
+ threadFail(detail);
+ } finally {
+ // Interrupt thread __after__ having reported its stack trace
+ thread.interrupt();
}
}
}
--- a/test/jdk/java/util/concurrent/tck/LinkedBlockingDequeTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/LinkedBlockingDequeTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -631,7 +631,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(SIZE, q.size());
@@ -673,7 +673,7 @@
assertEquals(0, q.take());
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -690,26 +690,27 @@
q.put(new Object());
q.put(new Object());
long startTime = System.nanoTime();
+
assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
Thread.currentThread().interrupt();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -750,7 +751,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -802,29 +803,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
@@ -883,7 +881,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(SIZE, q.size());
@@ -918,7 +916,7 @@
assertEquals(capacity - 1, q.take());
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -935,26 +933,27 @@
q.putFirst(new Object());
q.putFirst(new Object());
long startTime = System.nanoTime();
+
assertFalse(q.offerFirst(new Object(), timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
Thread.currentThread().interrupt();
try {
- q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offerFirst(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offerFirst(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -986,7 +985,7 @@
}});
await(threadStarted);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1027,7 +1026,7 @@
}});
await(threadStarted);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1077,7 +1076,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1118,29 +1117,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+ q.pollFirst(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+ q.pollFirst(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1164,7 +1160,7 @@
Thread.currentThread().interrupt();
try {
- q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+ q.pollFirst(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
@@ -1174,6 +1170,7 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
@@ -1182,7 +1179,7 @@
assertTrue(q.offerFirst(zero, LONG_DELAY_MS, MILLISECONDS));
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
barrier.await();
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1240,7 +1237,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(SIZE, q.size());
@@ -1282,7 +1279,7 @@
assertEquals(0, q.take());
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -1299,24 +1296,25 @@
q.putLast(new Object());
q.putLast(new Object());
long startTime = System.nanoTime();
+
assertFalse(q.offerLast(new Object(), timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
Thread.currentThread().interrupt();
try {
- q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offerLast(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
pleaseInterrupt.countDown();
try {
- q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offerLast(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1358,7 +1356,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -1399,30 +1397,27 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(SIZE - i - 1,
q.pollLast(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+ q.pollLast(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+ q.pollLast(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
@@ -1447,7 +1442,7 @@
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
@@ -1468,7 +1463,7 @@
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
barrier.await();
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
--- a/test/jdk/java/util/concurrent/tck/LinkedBlockingQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/LinkedBlockingQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -318,7 +318,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(SIZE, q.size());
@@ -360,7 +360,7 @@
assertEquals(0, q.take());
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -376,28 +376,28 @@
public void realRun() throws InterruptedException {
q.put(new Object());
q.put(new Object());
+ long startTime = System.nanoTime();
- long startTime = System.nanoTime();
assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
Thread.currentThread().interrupt();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -438,7 +438,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -490,29 +490,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
--- a/test/jdk/java/util/concurrent/tck/LinkedTransferQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/LinkedTransferQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -254,7 +254,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -308,29 +308,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
@@ -344,16 +341,14 @@
final BlockingQueue<Integer> q = populatedQueue(SIZE);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
Thread.currentThread().interrupt();
for (int i = 0; i < SIZE; ++i)
- assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(i, (int) q.poll(randomTimeout(), randomTimeUnit()));
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
awaitTermination(t);
@@ -982,25 +977,23 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
Thread.currentThread().interrupt();
try {
- q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+ q.tryTransfer(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+ q.tryTransfer(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
checkEmpty(q);
--- a/test/jdk/java/util/concurrent/tck/LongAccumulatorTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/LongAccumulatorTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -150,7 +150,7 @@
= new LongAccumulator((x, y) -> x + y, 0L);
final int nThreads = ThreadLocalRandom.current().nextInt(1, 5);
final Phaser phaser = new Phaser(nThreads + 1);
- final int incs = 1_000_000;
+ final int incs = expensiveTests ? 1_000_000 : 100_000;
final long total = nThreads * incs/2L * (incs - 1); // Gauss
final Runnable task = () -> {
phaser.arriveAndAwaitAdvance();
--- a/test/jdk/java/util/concurrent/tck/MapTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/MapTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -122,6 +122,7 @@
*/
public void testBug8186171() {
if (!impl.supportsSetValue()) return;
+ if (!atLeastJava10()) return; // jdk9 is no longer maintained
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
final boolean permitsNullValues = impl.permitsNullValues();
final Object v1 = (permitsNullValues && rnd.nextBoolean())
--- a/test/jdk/java/util/concurrent/tck/PhaserTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/PhaserTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -480,7 +480,7 @@
/**
* awaitAdvanceInterruptibly blocks interruptibly
*/
- public void testAwaitAdvanceInterruptibly_interruptible() throws InterruptedException {
+ public void testAwaitAdvanceInterruptibly_Interruptible() throws InterruptedException {
final Phaser phaser = new Phaser(1);
final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
@@ -505,14 +505,14 @@
public void realRun() throws TimeoutException {
Thread.currentThread().interrupt();
try {
- phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+ phaser.awaitAdvanceInterruptibly(0, randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+ phaser.awaitAdvanceInterruptibly(0, LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
@@ -520,8 +520,8 @@
await(pleaseInterrupt);
assertState(phaser, 0, 1, 1);
- assertThreadBlocks(t1, Thread.State.WAITING);
- assertThreadBlocks(t2, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t1, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t2, Thread.State.TIMED_WAITING);
t1.interrupt();
t2.interrupt();
awaitTermination(t1);
--- a/test/jdk/java/util/concurrent/tck/PriorityBlockingQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/PriorityBlockingQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -346,7 +346,7 @@
}
/**
- * timed offer does not time out
+ * Queue is unbounded, so timed offer never times out
*/
public void testTimedOffer() {
final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
@@ -397,7 +397,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -449,29 +449,26 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
-
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
--- a/test/jdk/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -707,7 +707,7 @@
Runnable waiter = new CheckedRunnable() { public void realRun() {
threadsStarted.countDown();
try {
- MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
} catch (InterruptedException success) {}
ran.getAndIncrement();
}};
--- a/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -666,7 +666,7 @@
Runnable waiter = new CheckedRunnable() { public void realRun() {
threadsStarted.countDown();
try {
- MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
} catch (InterruptedException success) {}
ran.getAndIncrement();
}};
--- a/test/jdk/java/util/concurrent/tck/SemaphoreTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/SemaphoreTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -38,7 +38,6 @@
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
-import java.util.concurrent.ThreadLocalRandom;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -220,24 +219,22 @@
/**
* timed tryAcquire times out
*/
- public void testTryAcquire_timeout() {
- final boolean fair = ThreadLocalRandom.current().nextBoolean();
+ public void testTryAcquire_timeout() throws InterruptedException {
+ final boolean fair = randomBoolean();
final Semaphore s = new Semaphore(0, fair);
final long startTime = System.nanoTime();
- try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); }
- catch (InterruptedException e) { threadUnexpectedException(e); }
+ assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}
/**
* timed tryAcquire(N) times out
*/
- public void testTryAcquireN_timeout() {
- final boolean fair = ThreadLocalRandom.current().nextBoolean();
+ public void testTryAcquireN_timeout() throws InterruptedException {
+ final boolean fair = randomBoolean();
final Semaphore s = new Semaphore(2, fair);
final long startTime = System.nanoTime();
- try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); }
- catch (InterruptedException e) { threadUnexpectedException(e); }
+ assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}
--- a/test/jdk/java/util/concurrent/tck/SynchronousQueueTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/SynchronousQueueTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -45,7 +45,6 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadLocalRandom;
import junit.framework.Test;
@@ -166,7 +165,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -207,7 +206,7 @@
catch (InterruptedException e) { threadUnexpectedException(e); }
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.WAITING);
t.interrupt();
awaitTermination(t);
assertEquals(0, q.remainingCapacity());
@@ -217,32 +216,33 @@
* timed offer times out if elements not taken
*/
public void testTimedOffer() {
- final boolean fair = ThreadLocalRandom.current().nextBoolean();
+ final boolean fair = randomBoolean();
final SynchronousQueue q = new SynchronousQueue(fair);
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
long startTime = System.nanoTime();
+
assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
Thread.currentThread().interrupt();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+ q.offer(new Object(), LONGER_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -272,7 +272,7 @@
* timed poll with nonzero timeout times out if no active putter
*/
public void testTimedPoll() {
- final boolean fair = ThreadLocalRandom.current().nextBoolean();
+ final boolean fair = randomBoolean();
final SynchronousQueue q = new SynchronousQueue(fair);
final long startTime = System.nanoTime();
try { assertNull(q.poll(timeoutMillis(), MILLISECONDS)); }
@@ -285,7 +285,7 @@
* after offer succeeds; on interruption throws
*/
public void testTimedPollWithOffer() {
- final boolean fair = ThreadLocalRandom.current().nextBoolean();
+ final boolean fair = randomBoolean();
final SynchronousQueue q = new SynchronousQueue(fair);
final CountDownLatch pleaseOffer = new CountDownLatch(1);
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
@@ -301,7 +301,7 @@
Thread.currentThread().interrupt();
try {
- q.poll(LONG_DELAY_MS, MILLISECONDS);
+ q.poll(randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
@@ -323,7 +323,7 @@
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
--- a/test/jdk/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -796,7 +796,7 @@
Runnable waiter = new CheckedRunnable() { public void realRun() {
threadsStarted.countDown();
try {
- MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
} catch (InterruptedException success) {}
ran.getAndIncrement();
}};
@@ -1669,7 +1669,7 @@
l.add(latchAwaitingStringTask(latch));
l.add(null);
try {
- e.invokeAny(l, randomTimeout(), MILLISECONDS);
+ e.invokeAny(l, randomTimeout(), randomTimeUnit());
shouldThrow();
} catch (NullPointerException success) {}
latch.countDown();
--- a/test/jdk/java/util/concurrent/tck/ThreadPoolExecutorTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/ThreadPoolExecutorTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -699,7 +699,7 @@
Runnable waiter = new CheckedRunnable() { public void realRun() {
threadsStarted.countDown();
try {
- MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
} catch (InterruptedException success) {}
ran.getAndIncrement();
}};
--- a/test/jdk/java/util/concurrent/tck/TimeUnitTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/concurrent/tck/TimeUnitTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -56,243 +56,56 @@
return new TestSuite(TimeUnitTest.class);
}
- // (loops to 88888 check increments at all time divisions.)
-
- /**
- * convert correctly converts sample values across the units
- */
- public void testConvert() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*60*60*24,
- SECONDS.convert(t, DAYS));
- assertEquals(t*60*60,
- SECONDS.convert(t, HOURS));
- assertEquals(t*60,
- SECONDS.convert(t, MINUTES));
- assertEquals(t,
- SECONDS.convert(t, SECONDS));
- assertEquals(t,
- SECONDS.convert(1000L*t, MILLISECONDS));
- assertEquals(t,
- SECONDS.convert(1000000L*t, MICROSECONDS));
- assertEquals(t,
- SECONDS.convert(1000000000L*t, NANOSECONDS));
-
- assertEquals(1000L*t*60*60*24,
- MILLISECONDS.convert(t, DAYS));
- assertEquals(1000L*t*60*60,
- MILLISECONDS.convert(t, HOURS));
- assertEquals(1000L*t*60,
- MILLISECONDS.convert(t, MINUTES));
- assertEquals(1000L*t,
- MILLISECONDS.convert(t, SECONDS));
- assertEquals(t,
- MILLISECONDS.convert(t, MILLISECONDS));
- assertEquals(t,
- MILLISECONDS.convert(1000L*t, MICROSECONDS));
- assertEquals(t,
- MILLISECONDS.convert(1000000L*t, NANOSECONDS));
-
- assertEquals(1000000L*t*60*60*24,
- MICROSECONDS.convert(t, DAYS));
- assertEquals(1000000L*t*60*60,
- MICROSECONDS.convert(t, HOURS));
- assertEquals(1000000L*t*60,
- MICROSECONDS.convert(t, MINUTES));
- assertEquals(1000000L*t,
- MICROSECONDS.convert(t, SECONDS));
- assertEquals(1000L*t,
- MICROSECONDS.convert(t, MILLISECONDS));
- assertEquals(t,
- MICROSECONDS.convert(t, MICROSECONDS));
- assertEquals(t,
- MICROSECONDS.convert(1000L*t, NANOSECONDS));
-
- assertEquals(1000000000L*t*60*60*24,
- NANOSECONDS.convert(t, DAYS));
- assertEquals(1000000000L*t*60*60,
- NANOSECONDS.convert(t, HOURS));
- assertEquals(1000000000L*t*60,
- NANOSECONDS.convert(t, MINUTES));
- assertEquals(1000000000L*t,
- NANOSECONDS.convert(t, SECONDS));
- assertEquals(1000000L*t,
- NANOSECONDS.convert(t, MILLISECONDS));
- assertEquals(1000L*t,
- NANOSECONDS.convert(t, MICROSECONDS));
- assertEquals(t,
- NANOSECONDS.convert(t, NANOSECONDS));
+ void testConversion(TimeUnit x, TimeUnit y, long n, long expected) {
+ assertEquals(expected, x.convert(n, y));
+ switch (x) {
+ case NANOSECONDS: assertEquals(expected, y.toNanos(n)); break;
+ case MICROSECONDS: assertEquals(expected, y.toMicros(n)); break;
+ case MILLISECONDS: assertEquals(expected, y.toMillis(n)); break;
+ case SECONDS: assertEquals(expected, y.toSeconds(n)); break;
+ case MINUTES: assertEquals(expected, y.toMinutes(n)); break;
+ case HOURS: assertEquals(expected, y.toHours(n)); break;
+ case DAYS: assertEquals(expected, y.toDays(n)); break;
+ default: throw new AssertionError();
}
- for (TimeUnit x : TimeUnit.values()) {
- long[] zs = {
- 0, 1, -1,
- Integer.MAX_VALUE, Integer.MIN_VALUE,
- Long.MAX_VALUE, Long.MIN_VALUE,
- };
- for (long z : zs) assertEquals(z, x.convert(z, x));
- }
+ if (n > 0) testConversion(x, y, -n, -expected);
}
- /**
- * toNanos correctly converts sample values in different units to
- * nanoseconds
- */
- public void testToNanos() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*1000000000L*60*60*24,
- DAYS.toNanos(t));
- assertEquals(t*1000000000L*60*60,
- HOURS.toNanos(t));
- assertEquals(t*1000000000L*60,
- MINUTES.toNanos(t));
- assertEquals(1000000000L*t,
- SECONDS.toNanos(t));
- assertEquals(1000000L*t,
- MILLISECONDS.toNanos(t));
- assertEquals(1000L*t,
- MICROSECONDS.toNanos(t));
- assertEquals(t,
- NANOSECONDS.toNanos(t));
+ void testConversion(TimeUnit x, TimeUnit y) {
+ long ratio = x.toNanos(1)/y.toNanos(1);
+ assertTrue(ratio > 0);
+ long[] ns = { 0, 1, 2, Long.MAX_VALUE/ratio, Long.MIN_VALUE/ratio };
+ for (long n : ns) {
+ testConversion(y, x, n, n * ratio);
+ long[] ks = { n * ratio, n * ratio + 1, n * ratio - 1 };
+ for (long k : ks) {
+ testConversion(x, y, k, k / ratio);
+ }
}
}
/**
- * toMicros correctly converts sample values in different units to
- * microseconds
- */
- public void testToMicros() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*1000000L*60*60*24,
- DAYS.toMicros(t));
- assertEquals(t*1000000L*60*60,
- HOURS.toMicros(t));
- assertEquals(t*1000000L*60,
- MINUTES.toMicros(t));
- assertEquals(1000000L*t,
- SECONDS.toMicros(t));
- assertEquals(1000L*t,
- MILLISECONDS.toMicros(t));
- assertEquals(t,
- MICROSECONDS.toMicros(t));
- assertEquals(t,
- NANOSECONDS.toMicros(t*1000L));
- }
- }
-
- /**
- * toMillis correctly converts sample values in different units to
- * milliseconds
+ * Conversion methods correctly convert sample values
*/
- public void testToMillis() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*1000L*60*60*24,
- DAYS.toMillis(t));
- assertEquals(t*1000L*60*60,
- HOURS.toMillis(t));
- assertEquals(t*1000L*60,
- MINUTES.toMillis(t));
- assertEquals(1000L*t,
- SECONDS.toMillis(t));
- assertEquals(t,
- MILLISECONDS.toMillis(t));
- assertEquals(t,
- MICROSECONDS.toMillis(t*1000L));
- assertEquals(t,
- NANOSECONDS.toMillis(t*1000000L));
- }
- }
-
- /**
- * toSeconds correctly converts sample values in different units to
- * seconds
- */
- public void testToSeconds() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*60*60*24,
- DAYS.toSeconds(t));
- assertEquals(t*60*60,
- HOURS.toSeconds(t));
- assertEquals(t*60,
- MINUTES.toSeconds(t));
- assertEquals(t,
- SECONDS.toSeconds(t));
- assertEquals(t,
- MILLISECONDS.toSeconds(t*1000L));
- assertEquals(t,
- MICROSECONDS.toSeconds(t*1000000L));
- assertEquals(t,
- NANOSECONDS.toSeconds(t*1000000000L));
- }
- }
+ public void testConversions() {
+ // Sanity check
+ assertEquals(1, NANOSECONDS.toNanos(1));
+ assertEquals(1000L * NANOSECONDS.toNanos(1), MICROSECONDS.toNanos(1));
+ assertEquals(1000L * MICROSECONDS.toNanos(1), MILLISECONDS.toNanos(1));
+ assertEquals(1000L * MILLISECONDS.toNanos(1), SECONDS.toNanos(1));
+ assertEquals(60L * SECONDS.toNanos(1), MINUTES.toNanos(1));
+ assertEquals(60L * MINUTES.toNanos(1), HOURS.toNanos(1));
+ assertEquals(24L * HOURS.toNanos(1), DAYS.toNanos(1));
- /**
- * toMinutes correctly converts sample values in different units to
- * minutes
- */
- public void testToMinutes() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*60*24,
- DAYS.toMinutes(t));
- assertEquals(t*60,
- HOURS.toMinutes(t));
- assertEquals(t,
- MINUTES.toMinutes(t));
- assertEquals(t,
- SECONDS.toMinutes(t*60));
- assertEquals(t,
- MILLISECONDS.toMinutes(t*1000L*60));
- assertEquals(t,
- MICROSECONDS.toMinutes(t*1000000L*60));
- assertEquals(t,
- NANOSECONDS.toMinutes(t*1000000000L*60));
+ for (TimeUnit x : TimeUnit.values()) {
+ assertEquals(x.toNanos(1), NANOSECONDS.convert(1, x));
}
- }
- /**
- * toHours correctly converts sample values in different units to
- * hours
- */
- public void testToHours() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t*24,
- DAYS.toHours(t));
- assertEquals(t,
- HOURS.toHours(t));
- assertEquals(t,
- MINUTES.toHours(t*60));
- assertEquals(t,
- SECONDS.toHours(t*60*60));
- assertEquals(t,
- MILLISECONDS.toHours(t*1000L*60*60));
- assertEquals(t,
- MICROSECONDS.toHours(t*1000000L*60*60));
- assertEquals(t,
- NANOSECONDS.toHours(t*1000000000L*60*60));
- }
- }
-
- /**
- * toDays correctly converts sample values in different units to
- * days
- */
- public void testToDays() {
- for (long t = 0; t < 88888; ++t) {
- assertEquals(t,
- DAYS.toDays(t));
- assertEquals(t,
- HOURS.toDays(t*24));
- assertEquals(t,
- MINUTES.toDays(t*60*24));
- assertEquals(t,
- SECONDS.toDays(t*60*60*24));
- assertEquals(t,
- MILLISECONDS.toDays(t*1000L*60*60*24));
- assertEquals(t,
- MICROSECONDS.toDays(t*1000000L*60*60*24));
- assertEquals(t,
- NANOSECONDS.toDays(t*1000000000L*60*60*24));
- }
+ for (TimeUnit x : TimeUnit.values())
+ for (TimeUnit y : TimeUnit.values())
+ if (x.toNanos(1) >= y.toNanos(1))
+ testConversion(x, y);
}
/**
@@ -494,14 +307,21 @@
* toString returns name of unit
*/
public void testToString() {
+ assertEquals("NANOSECONDS", NANOSECONDS.toString());
+ assertEquals("MICROSECONDS", MICROSECONDS.toString());
+ assertEquals("MILLISECONDS", MILLISECONDS.toString());
assertEquals("SECONDS", SECONDS.toString());
+ assertEquals("MINUTES", MINUTES.toString());
+ assertEquals("HOURS", HOURS.toString());
+ assertEquals("DAYS", DAYS.toString());
}
/**
* name returns name of unit
*/
public void testName() {
- assertEquals("SECONDS", SECONDS.name());
+ for (TimeUnit x : TimeUnit.values())
+ assertEquals(x.toString(), x.name());
}
/**
@@ -512,10 +332,8 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
Object o = new Object();
- TimeUnit tu = MILLISECONDS;
-
try {
- tu.timedWait(o, LONG_DELAY_MS);
+ MILLISECONDS.timedWait(o, LONGER_DELAY_MS);
threadShouldThrow();
} catch (IllegalMonitorStateException success) {}
}});
@@ -531,12 +349,11 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
Object o = new Object();
- TimeUnit tu = MILLISECONDS;
Thread.currentThread().interrupt();
try {
synchronized (o) {
- tu.timedWait(o, LONG_DELAY_MS);
+ MILLISECONDS.timedWait(o, LONGER_DELAY_MS);
}
shouldThrow();
} catch (InterruptedException success) {}
@@ -545,7 +362,7 @@
pleaseInterrupt.countDown();
try {
synchronized (o) {
- tu.timedWait(o, LONG_DELAY_MS);
+ MILLISECONDS.timedWait(o, LONGER_DELAY_MS);
}
shouldThrow();
} catch (InterruptedException success) {}
@@ -553,7 +370,7 @@
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
@@ -565,28 +382,27 @@
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
final Thread s = newStartedThread(new CheckedInterruptedRunnable() {
public void realRun() throws InterruptedException {
- Thread.sleep(LONG_DELAY_MS);
+ Thread.sleep(LONGER_DELAY_MS);
}});
final Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- TimeUnit tu = MILLISECONDS;
Thread.currentThread().interrupt();
try {
- tu.timedJoin(s, LONG_DELAY_MS);
+ MILLISECONDS.timedJoin(s, LONGER_DELAY_MS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- tu.timedJoin(s, LONG_DELAY_MS);
+ MILLISECONDS.timedJoin(s, LONGER_DELAY_MS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
s.interrupt();
@@ -594,35 +410,46 @@
}
/**
- * timedSleep throws InterruptedException when interrupted
+ * timeUnit.sleep throws InterruptedException when interrupted
*/
public void testTimedSleep_Interruptible() {
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- TimeUnit tu = MILLISECONDS;
Thread.currentThread().interrupt();
try {
- tu.sleep(LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
pleaseInterrupt.countDown();
try {
- tu.sleep(LONG_DELAY_MS);
+ MILLISECONDS.sleep(LONGER_DELAY_MS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
}});
await(pleaseInterrupt);
- assertThreadBlocks(t, Thread.State.TIMED_WAITING);
+ if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING);
t.interrupt();
awaitTermination(t);
}
/**
+ * timeUnit.sleep(x) for x <= 0 does not sleep at all.
+ */
+ public void testTimedSleep_nonPositive() throws InterruptedException {
+ boolean interrupt = randomBoolean();
+ if (interrupt) Thread.currentThread().interrupt();
+ randomTimeUnit().sleep(0L);
+ randomTimeUnit().sleep(-1L);
+ randomTimeUnit().sleep(Long.MIN_VALUE);
+ if (interrupt) assertTrue(Thread.interrupted());
+ }
+
+ /**
* a deserialized/reserialized unit is the same instance
*/
public void testSerialization() throws Exception {
--- a/test/jdk/java/util/regex/RegExTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/java/util/regex/RegExTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -35,7 +35,7 @@
* 8027645 8035076 8039124 8035975 8074678 6854417 8143854 8147531 7071819
* 8151481 4867170 7080302 6728861 6995635 6736245 4916384 6328855 6192895
* 6345469 6988218 6693451 7006761 8140212 8143282 8158482 8176029 8184706
- * 8194667 8197462 8184692 8221431 8224789 8228352
+ * 8194667 8197462 8184692 8221431 8224789 8228352 8230829
*
* @library /test/lib
* @library /lib/testlibrary/java/lang
@@ -1070,6 +1070,22 @@
matcher.useAnchoringBounds(false);
if (matcher.find())
failCount++;
+
+ // JDK-8230829
+ pattern = Pattern.compile("\\ud800\\udc61");
+ matcher = pattern.matcher("\ud800\udc61");
+ matcher.region(0, 1);
+ if (matcher.find()) {
+ failCount++;
+ System.out.println("Matched a surrogate pair" +
+ " that crosses border of region");
+ }
+ if (!matcher.hitEnd()) {
+ failCount++;
+ System.out.println("Expected to hit the end when" +
+ " matching a surrogate pair crossing region");
+ }
+
report("Regions");
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+import org.testng.annotations.*;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.*;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static java.lang.Boolean.TRUE;
+import static java.lang.String.format;
+import static java.util.stream.Collectors.joining;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8230870
+ * @summary Test ZIP Filesystem behavior with ~64k entries
+ * @modules jdk.zipfs
+ * @run testng LargeEntriesTest
+ */
+public class LargeEntriesTest {
+
+ private static final Path HERE = Path.of(".");
+
+ /**
+ * Number of ZIP entries which results in the use of ZIP64
+ */
+ private static final int ZIP64_ENTRIES = 65535;
+
+ /**
+ * Classes and MANIFEST attribute used for invoking Main via java -jar
+ */
+ private static final String MANIFEST_MAIN_CLASS = "LargeEntriesTest$Main";
+ private static final String MAIN_CLASS = "LargeEntriesTest$Main.class";
+ private static final String THIS_CLASS = "LargeEntriesTest.class";
+
+ /**
+ * Number of entries included in the JAR file including META-INF,
+ * MANIFEST.MF, and the classes associated with this test
+ */
+ private static final int ADDITIONAL_JAR_ENTRIES = 4;
+
+ /**
+ * Value used for creating the required entries in a ZIP or JAR file
+ */
+ private static final String ZIP_FILE_VALUE = "US Open 2019";
+ private static final byte[] ZIP_FILE_ENTRY =
+ ZIP_FILE_VALUE.getBytes(StandardCharsets.UTF_8);
+
+ /**
+ * Location of the classes to be added to the JAR file
+ */
+ static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
+
+ private static final SecureRandom random = new SecureRandom();
+
+ /**
+ * Fields used for timing runs
+ */
+ private static int testNumberRunning;
+ private static long runningTestTime;
+ private static long startTestRunTime;
+ private static final double NANOS_IN_SECOND = 1_000_000_000.0;
+
+ @BeforeTest(enabled = false)
+ public void beforeTest() {
+ startTestRunTime = System.nanoTime();
+ }
+
+ @AfterTest(enabled = false)
+ public void afterTest() {
+ long endTestRunTime = System.nanoTime();
+ long duration = endTestRunTime - startTestRunTime;
+ System.out.printf("#### Completed test run, total running time: %.4f in seconds%n",
+ duration / NANOS_IN_SECOND);
+ }
+
+ @BeforeMethod(enabled = false)
+ public static void beforeMethod() {
+ runningTestTime = System.nanoTime();
+ System.out.printf("**** Starting test number: %s%n", testNumberRunning);
+ }
+
+ @AfterMethod(enabled = false)
+ public void afterMethod() {
+ long endRunningTestTime = System.nanoTime();
+ long duration = endRunningTestTime - runningTestTime;
+ System.out.printf("**** Completed test number: %s, Time: %.4f%n",
+ testNumberRunning, duration / NANOS_IN_SECOND);
+ testNumberRunning++;
+ }
+
+ /**
+ * Validate that you can create a ZIP file with and without compression
+ * and that the ZIP file is created using ZIP64 if there are 65535 or
+ * more entries.
+ *
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param compression Indicates whether the files are DEFLATED(default)
+ * or STORED
+ * @throws Exception If an error occurs during the creation, verification or
+ * deletion of the ZIP file
+ */
+ @Test(dataProvider = "zipfsMap", enabled = true)
+ public void testZip(Map<String, String> env, int compression) throws Exception {
+
+ System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ",
+ formatMap(env), compression);
+
+ for (int entries = ZIP64_ENTRIES - 1; entries < ZIP64_ENTRIES + 2; entries++) {
+ Path zipfile = generatePath(HERE, "test", ".zip");
+ Files.deleteIfExists(zipfile);
+ createZipFile(zipfile, env, entries);
+ verify(zipfile, compression, entries,
+ isTrue(env, "forceZIP64End"), 0);
+ Files.deleteIfExists(zipfile);
+ }
+ }
+
+ /**
+ * Validate that when the forceZIP64End property is set to true,
+ * that ZIP64 is used.
+ *
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param compression Indicates whether the files are DEFLATED(default)
+ * or STORED
+ * @throws Exception If an error occurs during the creation, verification or
+ * deletion of the ZIP file
+ */
+ @Test(dataProvider = "zip64Map", enabled = true)
+ public void testForceZIP64End(Map<String, String> env, int compression) throws Exception {
+
+ System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ",
+ formatMap(env), compression);
+
+ // Generate a ZIP file path
+ Path zipfile = generatePath(HERE, "test", ".zip");
+ Files.deleteIfExists(zipfile);
+ createZipFile(zipfile, env, 1);
+ verify(zipfile, compression, 1, isTrue(env, "forceZIP64End"), 0);
+ Files.deleteIfExists(zipfile);
+ }
+
+ /**
+ * Validate that you can create a JAR file with and without compression
+ * and that the JAR file is created using ZIP64 if there are 65535 or
+ * more entries.
+ *
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param compression Indicates whether the files are DEFLATED(default)
+ * or STORED
+ * @throws Exception If an error occurs during the creation, verification or
+ * deletion of the JAR file
+ */
+ @Test(dataProvider = "zipfsMap", enabled = true)
+ public void testJar(Map<String, String> env, int compression) throws Exception {
+ for (int entries = ZIP64_ENTRIES - 1; entries < ZIP64_ENTRIES + 2; entries++) {
+ Path jar = generatePath(HERE, "test", ".jar");
+
+ Files.deleteIfExists(jar);
+ createJarFile(jar, env, entries);
+
+ // Now run the Main-Class specified the Manifest
+ runJar(jar.getFileName().toString()).assertSuccess()
+ .validate(r -> assertTrue(r.output.matches("\\AMain\\Z")));
+
+ verify(jar, compression, entries, isTrue(env, "forceZIP64End"),
+ ADDITIONAL_JAR_ENTRIES);
+ Files.deleteIfExists(jar);
+ }
+ }
+
+ /**
+ * Create a ZIP File System using the specified properties and a ZIP file
+ * with the specified number of entries
+ *
+ * @param zipFile Path to the ZIP File to create
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param entries Number of entries to add to the ZIP File
+ * @throws IOException If an error occurs while creating the ZIP file
+ */
+ private void createZipFile(Path zipFile, Map<String, String> env,
+ int entries) throws IOException {
+ System.out.printf("Creating file = %s%n", zipFile);
+ try (FileSystem zipfs =
+ FileSystems.newFileSystem(zipFile, env)) {
+
+ for (int i = 0; i < entries; i++) {
+ Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE);
+ }
+ }
+ }
+
+ /**
+ * Create a ZIP File System using the specified properties and a JAR file
+ * with the specified number of entries
+ *
+ * @param zipFile Path to the JAR File to create
+ * @param env Properties used for creating the ZIP Filesystem
+ * @param entries Number of entries to add to the JAR File
+ * @throws IOException If an error occurs while creating the JAR file
+ */
+ private void createJarFile(Path zipFile, Map<String, String> env,
+ int entries) throws IOException {
+ System.out.printf("Creating file = %s%n", zipFile);
+ String jdkVendor = System.getProperty("java.vendor");
+ String jdkVersion = System.getProperty("java.version");
+ String manifest = "Manifest-Version: 1.0"
+ + System.lineSeparator()
+ + "Main-Class: " + MANIFEST_MAIN_CLASS
+ + System.lineSeparator()
+ + "Created-By: " + jdkVersion + " (" + jdkVendor + ")";
+
+ try (FileSystem zipfs =
+ FileSystems.newFileSystem(zipFile, env);
+ InputStream in = new ByteArrayInputStream(manifest.getBytes())) {
+
+ // Get ZIP FS path to META-INF/MANIFEST.MF
+ Path metadir = zipfs.getPath("/", "META-INF");
+ Path manifestFile = metadir.resolve("MANIFEST.MF");
+
+ // Create META-INF directory if it does not already exist and
+ // add the MANIFEST.MF file
+ if (!Files.exists(metadir))
+ Files.createDirectory(zipfs.getPath("/", "META-INF"));
+ Files.copy(in, manifestFile);
+
+ // Add the needed test classes
+ Path target = zipfs.getPath("/");
+ Files.copy(TEST_CLASSES.resolve(MAIN_CLASS),
+ target.resolve(MAIN_CLASS));
+ Files.copy(TEST_CLASSES.resolve(THIS_CLASS),
+ target.resolve(THIS_CLASS));
+
+ // Add the remaining entries that are required
+ for (int i = ADDITIONAL_JAR_ENTRIES; i < entries; i++) {
+ Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE);
+ }
+ }
+ }
+
+ /*
+ * DataProvider used to validate that you can create a ZIP file with and
+ * without compression.
+ */
+ @DataProvider(name = "zipfsMap")
+ private Object[][] zipfsMap() {
+ return new Object[][]{
+ {Map.of("create", "true"), ZipEntry.DEFLATED},
+ {Map.of("create", "true", "noCompression", "true"),
+ ZipEntry.STORED},
+ {Map.of("create", "true", "noCompression", "false"),
+ ZipEntry.DEFLATED}
+ };
+ }
+
+ /*
+ * DataProvider used to validate that you can create a ZIP file with/without
+ * ZIP64 format extensions
+ */
+ @DataProvider(name = "zip64Map")
+ private Object[][] zip64Map() {
+ return new Object[][]{
+ {Map.of("create", "true", "forceZIP64End", "true"),
+ ZipEntry.DEFLATED},
+ {Map.of("create", "true", "noCompression", "true",
+ "forceZIP64End", "true"), ZipEntry.STORED},
+ {Map.of("create", "true", "noCompression", "false",
+ "forceZIP64End", "false"), ZipEntry.DEFLATED},
+ {Map.of("create", "true", "noCompression", "true",
+ "forceZIP64End", "false"), ZipEntry.STORED}
+ };
+ }
+
+ /**
+ * Verify that the given path is a ZIP file containing the
+ * expected entries.
+ *
+ * @param zipfile ZIP file to be validated
+ * @param method Expected Compression method: STORED or DEFLATED
+ * @param entries Number of expected entries
+ * @param isZip64Forced true if ZIP64 use is being forced; false otherwise
+ * @param start Starting number for verifying entries
+ * @throws Exception If an error occurs while examining the ZIP file
+ */
+ private static void verify(Path zipfile, int method, int entries,
+ boolean isZip64Forced, int start) throws Exception {
+ // check entries with ZIP API
+ try (ZipFile zf = new ZipFile(zipfile.toFile())) {
+ // check entry count
+ assertEquals(entries, zf.size());
+
+ // check compression method and content of each entry
+ for (int i = start; i < entries; i++) {
+ ZipEntry ze = zf.getEntry("Entry-" + i);
+ assertNotNull(ze);
+ assertEquals(method, ze.getMethod());
+ try (InputStream is = zf.getInputStream(ze)) {
+ byte[] bytes = is.readAllBytes();
+ assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY));
+ }
+ }
+ }
+ // check entries with FileSystem API
+ try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
+
+ // check entry count
+ Path top = fs.getPath("/");
+ long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) ->
+ attrs.isRegularFile() || (attrs.isDirectory() &&
+ path.getFileName() != null &&
+ path.getFileName().toString().equals("META-INF")))
+ .count();
+ assertEquals(entries, count);
+
+ // check content of each entry
+ for (int i = start; i < entries; i++) {
+ Path file = fs.getPath("Entry-" + i);
+ byte[] bytes = Files.readAllBytes(file);
+ assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY));
+ }
+ }
+
+ // Check for a ZIP64 End of Central Directory Locator
+ boolean foundZip64 = usesZip64(zipfile.toFile());
+
+ // Is ZIP64 required?
+ boolean requireZip64 = entries >= ZIP64_ENTRIES || isZip64Forced;
+ System.out.printf(" isZip64Forced = %s, foundZip64= %s, requireZip64= %s%n",
+ isZip64Forced, foundZip64, requireZip64);
+ assertEquals(requireZip64, foundZip64);
+
+
+ }
+
+ /**
+ * Determine if the specified property name=true/"true"
+ *
+ * @param env ZIP Filesystem Map
+ * @param name property to validate
+ * @return true if the property value is set to true/"true"; false otherwise
+ */
+ private static boolean isTrue(Map<String, ?> env, String name) {
+ return "true".equals(env.get(name)) || TRUE.equals(env.get(name));
+ }
+
+ /**
+ * Check to see if the ZIP64 End of Central Directory Locator has been found
+ *
+ * @param b byte array to check for the locator in
+ * @param n starting offset for the search
+ * @return true if the Zip64 End of Central Directory Locator is found; false
+ * otherwise
+ */
+ private static boolean end64SigAt(byte[] b, int n) {
+ return b[n] == 'P' & b[n + 1] == 'K' & b[n + 2] == 6 & b[n + 3] == 6;
+ }
+
+ /**
+ * Utility method that checks the ZIP file for the use of the ZIP64
+ * End of Central Directory Locator
+ *
+ * @param zipFile ZIP file to check
+ * @return true if the ZIP64 End of Central Directory Locator is found; false
+ * otherwise
+ * * @throws Exception If an error occurs while traversing the file
+ */
+ private static boolean usesZip64(File zipFile) throws Exception {
+
+ try (RandomAccessFile raf = new RandomAccessFile(zipFile, "r")) {
+ byte[] buf = new byte[4096];
+ long seeklen = raf.length() - buf.length;
+
+ if (seeklen < 0)
+ seeklen = 0;
+ raf.seek(seeklen);
+ raf.read(buf);
+ for (int i = 0; i < buf.length - 4; i++) {
+ // Is there a ZIP64 End of Central Directory Locator?
+ if (end64SigAt(buf, i)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Generate a temporary file Path
+ *
+ * @param dir Directory used to create the path
+ * @param prefix The prefix string used to create the path
+ * @param suffix The suffix string used to create the path
+ * @return Path that was generated
+ */
+ private static Path generatePath(Path dir, String prefix, String suffix) {
+ long n = random.nextLong();
+ String s = prefix + Long.toUnsignedString(n) + suffix;
+ Path name = dir.getFileSystem().getPath(s);
+ // the generated name should be a simple file name
+ if (name.getParent() != null)
+ throw new IllegalArgumentException("Invalid prefix or suffix");
+ return dir.resolve(name);
+ }
+
+ /**
+ * Utility method to return a formatted String of the key:value entries for
+ * a Map
+ *
+ * @param env Map to format
+ * @return Formatted string of the Map entries
+ */
+ private static String formatMap(Map<String, String> env) {
+ return env.entrySet().stream()
+ .map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
+ .collect(joining(", "));
+ }
+
+ /**
+ * Validates that a jar created using ZIP FS can be used by the java
+ * tool to run a program specified in the Main-Class Manifest attribute
+ *
+ * @param jarFile Name of the JAR file to specify to the -jar option
+ * @return A Result object representing the return code and output from the
+ * program that was invoked
+ */
+ private static Result runJar(String jarFile) {
+ String javaHome = System.getProperty("java.home");
+ String java = Paths.get(javaHome, "bin", "java").toString();
+ String[] cmd = {java, "-jar", jarFile};
+ String output;
+ ProcessBuilder pb = new ProcessBuilder(cmd);
+ Process p;
+ try {
+ p = pb.start();
+ output = toString(p.getInputStream(), p.getErrorStream());
+ p.waitFor();
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(
+ format("Error invoking: '%s', Exception= %s", pb.command(), e));
+ }
+
+ return new Result(p.exitValue(), output);
+ }
+
+ /**
+ * Utility method to combine the output and error streams for the Process
+ * started by ProcessBuilder
+ *
+ * @param is Process Outputstream
+ * @param is2 Process ErrorStream
+ * @return String representing the combination of the OutputStream & ErrorStream
+ * @throws IOException If an error occurs while combining the streams
+ */
+ private static String toString(InputStream is, InputStream is2) throws IOException {
+ try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
+ InputStream concatenated = new SequenceInputStream(is, is2)) {
+ concatenated.transferTo(dst);
+ return new String(dst.toByteArray(), StandardCharsets.UTF_8);
+ }
+ }
+
+ /**
+ * Wrapper class used to verify the results from a ProcessBuilder invocation
+ */
+ private static class Result {
+ final int ec; // Return code for command that was executed
+ final String output; // Output from the command that was executed
+
+ /**
+ * Constructor
+ *
+ * @param ec Return code from the ProcessBuilder invocation
+ * @param output ProcessBuilder output to be validated
+ */
+ private Result(int ec, String output) {
+ this.ec = ec;
+ this.output = output;
+ }
+
+ /**
+ * Validate that the command that was executed completed successfully
+ *
+ * @return This Result object
+ */
+ Result assertSuccess() {
+ assertEquals(ec, 0, format("Expected ec 0, received: %s, output [%s]", ec, output));
+ return this;
+ }
+
+ /**
+ * Validate that the expected result is received
+ *
+ * @param r The operation to perform
+ * @return This Result object
+ */
+ Result validate(Consumer<Result> r) {
+ r.accept(this);
+ return this;
+ }
+ }
+
+ /**
+ * Trivial class used to validate that a JAR created using ZIP FS
+ * can be successfully executed
+ */
+ public static class Main {
+ public static void main(String[] args) {
+ System.out.print("Main");
+ }
+ }
+}
--- a/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -62,7 +62,7 @@
public static void startHttpServer() {
try {
httpTrans = new SimpleHttpTransaction();
- server = new TestHttpServer(httpTrans, 1, 10, 0);
+ server = new TestHttpServer(httpTrans, 1, 10, InetAddress.getLocalHost(), 0);
} catch (IOException e) {
e.printStackTrace();
}
@@ -71,13 +71,14 @@
public static void clientHttpCalls() {
try {
System.out.println("http server listen on: " + server.getLocalPort());
- String baseURLStr = "http://" + InetAddress.getLocalHost().getHostAddress() + ":" +
- server.getLocalPort() + "/";
+ String hostAddr = InetAddress.getLocalHost().getHostAddress();
+ if (hostAddr.indexOf(':') > -1) hostAddr = "[" + hostAddr + "]";
+ String baseURLStr = "http://" + hostAddr + ":" + server.getLocalPort() + "/";
URL bigDataURL = new URL (baseURLStr + "firstCall");
URL smallDataURL = new URL (baseURLStr + "secondCall");
- HttpURLConnection uc = (HttpURLConnection)bigDataURL.openConnection();
+ HttpURLConnection uc = (HttpURLConnection)bigDataURL.openConnection(Proxy.NO_PROXY);
//Only read 1 byte of response data and close the stream
InputStream is = uc.getInputStream();
@@ -88,7 +89,7 @@
// Allow the KeepAliveStreamCleaner thread to read the data left behind and cache the connection.
try { Thread.sleep(2000); } catch (Exception e) {}
- uc = (HttpURLConnection)smallDataURL.openConnection();
+ uc = (HttpURLConnection)smallDataURL.openConnection(Proxy.NO_PROXY);
uc.getResponseCode();
if (SimpleHttpTransaction.failed)
@@ -96,7 +97,7 @@
// Part 2
URL part2Url = new URL (baseURLStr + "part2");
- uc = (HttpURLConnection)part2Url.openConnection();
+ uc = (HttpURLConnection)part2Url.openConnection(Proxy.NO_PROXY);
is = uc.getInputStream();
is.close();
--- a/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -42,7 +42,10 @@
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Proxy;
import java.net.URL;
+import java.net.UnknownHostException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
@@ -64,6 +67,10 @@
(new ServerIdentityTest()).run();
}
+ ServerIdentityTest() throws UnknownHostException {
+ serverAddress = InetAddress.getByName(hostname);
+ }
+
@Override
protected boolean isCustomizedClientConnection() {
return true;
@@ -88,7 +95,7 @@
HttpURLConnection urlc = null;
InputStream is = null;
try {
- urlc = (HttpURLConnection)url.openConnection();
+ urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
is = urlc.getInputStream();
} finally {
if (is != null) {
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -651,8 +651,13 @@
serverModulus, serverPrivateExponent, passphrase);
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ // doClientSide() connects to "localhost"
+ InetAddress localHost = InetAddress.getByName("localhost");
+ InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
+
sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
/*
@@ -717,7 +722,7 @@
System.out.println("url is "+url.toString());
try {
- http = (HttpsURLConnection)url.openConnection();
+ http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
int respCode = http.getResponseCode();
System.out.println("respCode = "+respCode);
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressDNSIdentities.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressDNSIdentities.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -650,8 +650,13 @@
serverModulus, serverPrivateExponent, passphrase);
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ // doClientSide() connects to the loopback address
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ InetSocketAddress address = new InetSocketAddress(loopback, serverPort);
+
sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
/*
@@ -721,7 +726,7 @@
System.out.println("url is "+url.toString());
try {
- http = (HttpsURLConnection)url.openConnection();
+ http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
int respCode = http.getResponseCode();
System.out.println("respCode = " + respCode);
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -654,8 +654,13 @@
serverModulus, serverPrivateExponent, passphrase);
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ // doClientSide() connects to the loopback address
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ InetSocketAddress address = new InetSocketAddress(loopback, serverPort);
+
sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
/*
@@ -725,7 +730,7 @@
System.out.println("url is "+url.toString());
try {
- http = (HttpsURLConnection)url.openConnection();
+ http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
int respCode = http.getResponseCode();
System.out.println("respCode = "+respCode);
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java Wed Sep 18 07:46:02 2019 +0200
@@ -29,6 +29,7 @@
/* @test
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
* @bug 6766775
+ * @library /test/lib
* @run main/othervm IPIdentities
* @author Xuelei Fan
*/
@@ -45,6 +46,7 @@
import java.security.spec.*;
import java.security.interfaces.*;
import java.math.BigInteger;
+import jdk.test.lib.net.URIBuilder;
/*
* Certificates and key used in the test.
@@ -652,8 +654,13 @@
serverModulus, serverPrivateExponent, passphrase);
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ // doClientSide() connects to the loopback address
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ InetSocketAddress address = new InetSocketAddress(loopback, serverPort);
+
sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
/*
@@ -713,11 +720,16 @@
HttpsURLConnection http = null;
/* establish http connection to server */
- URL url = new URL("https://localhost:" + serverPort+"/");
+ URL url = URIBuilder.newBuilder()
+ .scheme("https")
+ .loopback()
+ .port(serverPort)
+ .path("/")
+ .toURL();
System.out.println("url is "+url.toString());
try {
- http = (HttpsURLConnection)url.openConnection();
+ http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
int respCode = http.getResponseCode();
System.out.println("respCode = "+respCode);
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -651,8 +651,13 @@
serverModulus, serverPrivateExponent, passphrase);
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ // doClientSide() connects to "localhost"
+ InetAddress localHost = InetAddress.getByName("localhost");
+ InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
+
sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
/*
@@ -717,7 +722,7 @@
System.out.println("url is "+url.toString());
try {
- http = (HttpsURLConnection)url.openConnection();
+ http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
int respCode = http.getResponseCode();
System.out.println("respCode = "+respCode);
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ImpactOnSNI.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ImpactOnSNI.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -95,6 +95,16 @@
* smart about it....
*/
+ private SSLServerSocket createServerSocket(SSLServerSocketFactory sslssf)
+ throws Exception {
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket)sslssf.createServerSocket();
+ InetAddress localHost = InetAddress.getLocalHost();
+ InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
+ sslServerSocket.bind(address);
+ return sslServerSocket;
+ }
+
/*
* Define the server side of the test.
*
@@ -104,8 +114,7 @@
private void doServerSide() throws Exception {
SSLServerSocketFactory sslssf =
(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
- try (SSLServerSocket sslServerSocket =
- (SSLServerSocket)sslssf.createServerSocket(serverPort)) {
+ try (SSLServerSocket sslServerSocket = createServerSocket(sslssf)) {
serverPort = sslServerSocket.getLocalPort();
--- a/test/jdk/sun/net/www/protocol/https/NewImpl/JavaxHostnameVerifier.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/https/NewImpl/JavaxHostnameVerifier.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -135,8 +135,14 @@
SSLServerSocketFactory sslssf =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+
+ // doClientSide() connects to "localhost"
+ InetAddress localHost = InetAddress.getByName("localhost");
+ InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
+
SSLServerSocket sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
+ (SSLServerSocket) sslssf.createServerSocket();
+ sslServerSocket.bind(address);
serverPort = sslServerSocket.getLocalPort();
String ciphers[]= { "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA" };
@@ -205,7 +211,7 @@
URL url = new URL("https://" + "localhost:" + serverPort +
"/etc/hosts");
- URLConnection urlc = url.openConnection();
+ URLConnection urlc = url.openConnection(Proxy.NO_PROXY);
if (!(urlc instanceof javax.net.ssl.HttpsURLConnection)) {
throw new Exception(
--- a/test/jdk/sun/net/www/protocol/jar/B4957695.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/net/www/protocol/jar/B4957695.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -96,7 +96,10 @@
public static void main (String[] args) throws Exception {
String tmpdir = System.getProperty("java.io.tmpdir");
String[] list1 = listTmpFiles(tmpdir);
- ServerSocket serverSocket = new ServerSocket(0);
+ InetAddress localHost = InetAddress.getByName("localhost");
+ InetSocketAddress address = new InetSocketAddress(localHost, 0);
+ ServerSocket serverSocket = new ServerSocket();
+ serverSocket.bind(address);
server = new Server(serverSocket);
server.start();
int port = serverSocket.getLocalPort();
@@ -108,7 +111,9 @@
read (is);
is.close();
} catch (IOException e) {
- System.out.println ("Received IOException as expected");
+ System.out.println ("Received IOException as expected: " + e);
+ } finally {
+ try {serverSocket.close();} catch (IOException x) {}
}
String[] list2 = listTmpFiles(tmpdir);
if (!sameList (list1, list2)) {
--- a/test/jdk/sun/tools/jcmd/TestProcessHelper.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/jdk/sun/tools/jcmd/TestProcessHelper.java Wed Sep 18 07:46:02 2019 +0200
@@ -189,6 +189,26 @@
private void checkMainClass(Process p, String expectedMainClass) {
String mainClass = PROCESS_HELPER.getMainClass(Long.toString(p.pid()));
+ // getMainClass() may return null, e.g. due to timing issues.
+ // Attempt some limited retries.
+ if (mainClass == null) {
+ System.err.println("Main class returned by ProcessHelper was null.");
+ // sleep time doubles each round, altogether, wait no longer than 1 sec
+ final int MAX_RETRIES = 10;
+ int retrycount = 0;
+ long sleepms = 1;
+ while (retrycount < MAX_RETRIES && mainClass == null) {
+ System.err.println("Retry " + retrycount + ", sleeping for " + sleepms + "ms.");
+ try {
+ Thread.sleep(sleepms);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mainClass = PROCESS_HELPER.getMainClass(Long.toString(p.pid()));
+ retrycount++;
+ sleepms *= 2;
+ }
+ }
p.destroyForcibly();
if (!expectedMainClass.equals(mainClass)) {
throw new RuntimeException("Main class is wrong: " + mainClass);
--- a/test/lib/jdk/test/lib/Platform.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/lib/jdk/test/lib/Platform.java Wed Sep 18 07:46:02 2019 +0200
@@ -265,7 +265,6 @@
return false;
}
} catch (PrivilegedActionException e) {
- @SuppressWarnings("unchecked")
IOException t = (IOException) e.getException();
throw t;
}
@@ -289,7 +288,6 @@
return false;
}
} catch (PrivilegedActionException e) {
- @SuppressWarnings("unchecked")
IOException t = (IOException) e.getException();
throw t;
}
--- a/test/lib/jdk/test/lib/Utils.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/lib/jdk/test/lib/Utils.java Wed Sep 18 07:46:02 2019 +0200
@@ -25,6 +25,7 @@
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Annotation;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
@@ -749,13 +750,14 @@
// until the method main() is found; the class containing that method is the
// main test class and will be returned as the name of the test.
// Special handling is used for testng tests.
+ @SuppressWarnings("unchecked")
public static String getTestName() {
String result = null;
// If we are using testng, then we should be able to load the "Test" annotation.
- Class testClassAnnotation;
+ Class<? extends Annotation> testClassAnnotation;
try {
- testClassAnnotation = Class.forName("org.testng.annotations.Test");
+ testClassAnnotation = (Class<? extends Annotation>)Class.forName("org.testng.annotations.Test");
} catch (ClassNotFoundException e) {
testClassAnnotation = null;
}
@@ -776,7 +778,7 @@
// annotation. If present, then use the name of this class.
if (testClassAnnotation != null) {
try {
- Class c = Class.forName(className);
+ Class<?> c = Class.forName(className);
if (c.isAnnotationPresent(testClassAnnotation)) {
result = className;
break;
--- a/test/lib/jdk/test/lib/process/ProcessTools.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/lib/jdk/test/lib/process/ProcessTools.java Wed Sep 18 07:46:02 2019 +0200
@@ -495,7 +495,6 @@
return AccessController.doPrivileged(
(PrivilegedExceptionAction<Process>) () -> pb.start());
} catch (PrivilegedActionException e) {
- @SuppressWarnings("unchecked")
IOException t = (IOException) e.getException();
throw t;
}
--- a/test/lib/jdk/test/lib/process/StreamPumper.java Thu Sep 12 15:04:00 2019 +0200
+++ b/test/lib/jdk/test/lib/process/StreamPumper.java Wed Sep 18 07:46:02 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -144,7 +144,9 @@
}
} catch (IOException e) {
- e.printStackTrace();
+ if (!e.getMessage().equalsIgnoreCase("stream closed")) {
+ e.printStackTrace();
+ }
} finally {
for (OutputStream out : outStreams) {
try {