# HG changeset patch # User dlong # Date 1539716807 25200 # Tue Oct 16 12:06:47 2018 -0700 # Node ID 5f833f6987ff6e86d8d770cdc722bde68093c2c5 # Parent 367b2cd49ec5efad2f89346d3c0d1ddcc115d70c [mq]: 8021335 diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1930,6 +1930,7 @@ } // no more external suspends are allowed at this point } else { + assert(!is_terminated() && !is_exiting(), "must not be exiting"); // before_exit() has already posted JVMTI THREAD_END events } @@ -4332,7 +4333,7 @@ void Threads::add(JavaThread* p, bool force_daemon) { // The threads lock must be owned at this point - assert_locked_or_safepoint(Threads_lock); + assert(Threads_lock->owned_by_self(), "must have threads lock"); BarrierSet::barrier_set()->on_thread_attach(p); diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -57,8 +57,8 @@ PerfVariable* ThreadService::_live_threads_count = NULL; PerfVariable* ThreadService::_peak_threads_count = NULL; PerfVariable* ThreadService::_daemon_threads_count = NULL; -volatile int ThreadService::_exiting_threads_count = 0; -volatile int ThreadService::_exiting_daemon_threads_count = 0; +volatile int ThreadService::_atomic_threads_count = 0; +volatile int ThreadService::_atomic_daemon_threads_count = 0; ThreadDumpResult* ThreadService::_threaddump_list = NULL; @@ -101,50 +101,104 @@ _peak_threads_count->set_value(get_live_thread_count()); } +static boolean is_hidden_thread(JavaThread *thread) { + return thread->is_hidden_from_external_view() || thread->is_jvmti_agent_thread(); +} + void ThreadService::add_thread(JavaThread* thread, bool daemon) { + assert(Threads_lock->owned_by_self(), "must have threads lock"); + // Do not count VM internal or JVMTI agent threads - if (thread->is_hidden_from_external_view() || - thread->is_jvmti_agent_thread()) { + if (is_hidden_thread(thread)) { return; } _total_threads_count->inc(); _live_threads_count->inc(); + Atomic::inc(&_atomic_threads_count); + int count = _atomic_threads_count; - if (_live_threads_count->get_value() > _peak_threads_count->get_value()) { - _peak_threads_count->set_value(_live_threads_count->get_value()); + if (count > _peak_threads_count->get_value()) { + _peak_threads_count->set_value(count); } if (daemon) { _daemon_threads_count->inc(); + Atomic::inc(&_atomic_daemon_threads_count); + } +} + +void ThreadService::decrement_thread_counts(JavaThread* jt) { + Atomic::dec(&_atomic_threads_count); + + oop threadObj = jt->threadObj(); + if (threadObj != NULL && java_lang_Thread::is_daemon(threadObj)) { + Atomic::dec(&_atomic_daemon_threads_count); } } void ThreadService::remove_thread(JavaThread* thread, bool daemon) { - Atomic::dec(&_exiting_threads_count); - if (daemon) { - Atomic::dec(&_exiting_daemon_threads_count); - } + assert(Threads_lock->owned_by_self(), "must have threads lock"); - if (thread->is_hidden_from_external_view() || - thread->is_jvmti_agent_thread()) { + // Do not count VM internal or JVMTI agent threads + if (is_hidden_thread(thread)) { return; } - _live_threads_count->set_value(_live_threads_count->get_value() - 1); + assert(!thread->is_terminated(), "must not be terminated"); + if (!thread->is_exiting()) { + // JavaThread::exit() skipped calling current_thread_exiting() + decrement_thread_counts(thread); + } + + int daemon_count = _atomic_daemon_threads_count; + int count = _atomic_threads_count; + + // Counts are incremented at the same time, but atomic counts are + // decremented earlier than perf counts. + assert(_live_threads_count->get_value() > count, + "thread count mismatch %d : %d", + (int)_live_threads_count->get_value(), count); + + _live_threads_count->dec(1); if (daemon) { - _daemon_threads_count->set_value(_daemon_threads_count->get_value() - 1); + assert(_daemon_threads_count->get_value() > daemon_count, + "thread count mismatch %d : %d", + (int)_daemon_threads_count->get_value(), daemon_count); + + _daemon_threads_count->dec(1); } + + // Counts are incremented at the same time, but atomic counts are + // decremented earlier than perf counts. + assert(_daemon_threads_count->get_value() >= daemon_count, + "thread count mismatch %d : %d", + (int)_daemon_threads_count->get_value(), daemon_count); + assert(_live_threads_count->get_value() >= count, + "thread count mismatch %d : %d", + (int)_live_threads_count->get_value(), count); + assert(_live_threads_count->get_value() > 0 || + (_live_threads_count->get_value() == 0 && count == 0 && + _daemon_threads_count->get_value() == 0 && daemon_count == 0), + "thread counts should reach 0 at the same time, live %d,%d daemon %d,%d", + (int)_live_threads_count->get_value(), count, + (int)_daemon_threads_count->get_value(), daemon_count); + assert(_daemon_threads_count->get_value() > 0 || + (_daemon_threads_count->get_value() == 0 && daemon_count == 0), + "thread counts should reach 0 at the same time, daemon %d,%d", + (int)_daemon_threads_count->get_value(), daemon_count); } void ThreadService::current_thread_exiting(JavaThread* jt) { + // Do not count VM internal or JVMTI agent threads + if (is_hidden_thread(jt)) { + return; + } + assert(jt == JavaThread::current(), "Called by current thread"); - Atomic::inc(&_exiting_threads_count); + assert(!jt->is_terminated() && jt->is_exiting(), "must be exiting"); - oop threadObj = jt->threadObj(); - if (threadObj != NULL && java_lang_Thread::is_daemon(threadObj)) { - Atomic::inc(&_exiting_daemon_threads_count); - } + decrement_thread_counts(jt); } // FIXME: JVMTI should call this function diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp --- a/src/hotspot/share/services/threadService.hpp +++ b/src/hotspot/share/services/threadService.hpp @@ -58,10 +58,12 @@ static PerfVariable* _peak_threads_count; static PerfVariable* _daemon_threads_count; - // These 2 counters are atomically incremented once the thread is exiting. - // They will be atomically decremented when ThreadService::remove_thread is called. - static volatile int _exiting_threads_count; - static volatile int _exiting_daemon_threads_count; + // These 2 counters are like the above thread counts, but are + // atomically decremented in ThreadService::current_thread_exiting instead of + // ThreadService::remove_thread, so that the thread count is updated before + // Thread.join() returns. + static volatile int _atomic_threads_count; + static volatile int _atomic_daemon_threads_count; static bool _thread_monitoring_contention_enabled; static bool _thread_cpu_time_enabled; @@ -72,6 +74,8 @@ // requested by multiple threads concurrently. static ThreadDumpResult* _threaddump_list; + static void decrement_thread_counts(JavaThread* jt); + public: static void init(); static void add_thread(JavaThread* thread, bool daemon); @@ -89,11 +93,8 @@ static jlong get_total_thread_count() { return _total_threads_count->get_value(); } static jlong get_peak_thread_count() { return _peak_threads_count->get_value(); } - static jlong get_live_thread_count() { return _live_threads_count->get_value() - _exiting_threads_count; } - static jlong get_daemon_thread_count() { return _daemon_threads_count->get_value() - _exiting_daemon_threads_count; } - - static int exiting_threads_count() { return _exiting_threads_count; } - static int exiting_daemon_threads_count() { return _exiting_daemon_threads_count; } + static jlong get_live_thread_count() { return _atomic_threads_count; } + static jlong get_daemon_thread_count() { return _atomic_daemon_threads_count; } // Support for thread dump static void add_thread_dump(ThreadDumpResult* dump); diff --git a/test/jdk/java/lang/management/ThreadMXBean/ResetPeakThreadCount.java b/test/jdk/java/lang/management/ThreadMXBean/ResetPeakThreadCount.java --- a/test/jdk/java/lang/management/ThreadMXBean/ResetPeakThreadCount.java +++ b/test/jdk/java/lang/management/ThreadMXBean/ResetPeakThreadCount.java @@ -158,10 +158,6 @@ " Expected to be == previous peak = " + peak1 + " + " + delta); } - // wait until the current thread count gets incremented - while (mbean.getThreadCount() < (current + count)) { - Thread.sleep(100); - } current = mbean.getThreadCount(); System.out.println(" Live thread count before returns " + current); return current; @@ -195,12 +191,6 @@ allThreads[i].join(); } - // there is a race in the counter update logic somewhere causing - // the thread counters go ff - // we need to give the terminated threads some extra time to really die - // JDK-8021335 - Thread.sleep(500); - long current = mbean.getThreadCount(); System.out.println(" Live thread count before returns " + current); return current;