On 21/09/2019 4:21 pm, serguei.spit...@oracle.com wrote:
Hi David,

I do not want to insist in the area (Threads_lock use) where you have much more expertise.

It's more conservatism than expertise :) I had a look through and there are not too many usages of the Threads_lock in the M&M code and they do appear safe. But I still prefer on principle to not hold nested locks if it is not necessary.

Also, I agreed that Daniil's webrev.07 version is correct and less risky.
The only my concern was about possible performance impact of linear searches from the ThreadsList::find_JavaThread_from_java_tid() calls that can be executed concurrently with the ThreadTable::lazy_initialize() if the ThreadsList is big.
But it should not be that big issue.

I think we can waste a lot of time wondering about what kinds of scenarios perform better or worse with which variant of the code. You will always be able to create a microbenchmark for which a particular code pattern works best. So I'd say stick to the simplest and least risky code and worry about performance issues like this if, or when, they turn up.

So, I'm okay with the webrev.07 modulo ThreadTable renaming.

Okay.

Thanks,
David

Thanks,
Serguei


On 9/20/19 19:20, David Holmes wrote:
Hi Serguei,

On 21/09/2019 9:50 am, serguei.spit...@oracle.com wrote:
Hi Daniil,

Yes, the Threads_lock is still needed around thread->is_exiting() check and add_thread().

And if we try to use 2 locks, ThreadTableCreate_lock as in your snippet
and then the nested Threads_lock around thread->is_exiting() and
add_thread(java_tid, thread) lines then it will not work since the rank
of Threads_lock  is higher than the rank of ThreadTableCreate_lock.

The ThreadTableCreate_lock is only used in the lazy_initialize() function. It looks safe to adjust the ThreadTableCreate_lock to fix the above problem.
Then the approach below will work as needed.

  void ThreadTable::lazy_initialize(const ThreadsList *threads) {
     if (_is_initialized) {
       return;
     }
     MutexLocker ml(ThreadTableCreate_lock);
     if (_is_initialized) {
       // There is no obvious benefits in allowing the thread table
       // being concurrently populated during the initalization.
       return;
     }
     create_table(threads->length());
     _is_initialized = true;

     for (uint i = 0; i < threads->length(); i++) {
       JavaThread* thread = threads->thread_at(i);
       oop tobj = thread->threadObj();
       if (tobj != NULL) {
         jlong java_tid = java_lang_Thread::thread_id(tobj);
         MutexLocker ml(Threads_lock);
         if (!thread->is_exiting()) {
           // Must be inside the lock to ensure that we don't add the thread to the table            // that has just passed the removal point in ThreadsSMRSupport::remove_thread()
           add_thread(java_tid, thread);
         }
       }
     }
   }

I don't like holding a completely unrelated lock, whilst holding the Threads_lock, particularly as it seems unnecessary for correctness. We have to be certain that no M&M operation that holds the Threads_lock can try to initialize the thread table. Testing can only prove the presence of that problem, not the absence.

I think Daniil's webrev.07 version is correct and less risky than what you propose. Sorry.

David
-----

Reply via email to