@@ -75,7 +75,7 @@ public final class DebuggerController implements ContextsListener {
7575 private final Map <Object , SimpleLock > suspendLocks = Collections .synchronizedMap (new HashMap <>());
7676 private final Map <Object , SuspendedInfo > suspendedInfos = Collections .synchronizedMap (new HashMap <>());
7777 private final Map <Object , SteppingInfo > commandRequestIds = new HashMap <>();
78- private final Map <Object , ThreadJob <?>> threadJobs = new HashMap <>();
78+ private final Map <Object , InvokeJob <?>> invokeJobs = new HashMap <>();
7979 private final Map <Object , FieldBreakpointEvent > fieldBreakpointExpected = new HashMap <>();
8080 private final Map <Object , MethodBreakpointEvent > methodBreakpointExpected = new HashMap <>();
8181 private final Map <Breakpoint , BreakpointInfo > breakpointInfos = new HashMap <>();
@@ -176,6 +176,10 @@ public JDWPContext getContext() {
176176 return context ;
177177 }
178178
179+ public Ids <Object > getIds () {
180+ return ids ;
181+ }
182+
179183 public SuspendedInfo getSuspendedInfo (Object thread ) {
180184 return suspendedInfos .get (thread );
181185 }
@@ -386,6 +390,7 @@ public Object[] getVisibleGuestThreads() {
386390 }
387391
388392 void forceResumeAll () {
393+ ids .unpinAll ();
389394 for (Object thread : getVisibleGuestThreads ()) {
390395 boolean resumed = false ;
391396 SimpleLock suspendLock = getSuspendLock (thread );
@@ -398,6 +403,7 @@ void forceResumeAll() {
398403 }
399404
400405 public void resumeAll () {
406+ ids .unpinAll ();
401407 for (Object thread : getVisibleGuestThreads ()) {
402408 SimpleLock suspendLock = getSuspendLock (thread );
403409 synchronized (suspendLock ) {
@@ -453,6 +459,9 @@ public void immediateSuspend(Object eventThread, byte suspendPolicy, Callable<Vo
453459 suspend (thread );
454460 }
455461 }
462+ // pin all objects when VM in suspended state
463+ ids .pinAll ();
464+
456465 // immediately suspend the event thread
457466 suspend (eventThread , SuspendStrategy .EVENT_THREAD , Collections .singletonList (callBack ), true );
458467 break ;
@@ -469,6 +478,8 @@ public void suspendAll() {
469478 for (Object thread : getVisibleGuestThreads ()) {
470479 suspend (thread );
471480 }
481+ // pin all objects
482+ ids .pinAll ();
472483 }
473484
474485 private synchronized SimpleLock getSuspendLock (Object thread ) {
@@ -480,7 +491,7 @@ private synchronized SimpleLock getSuspendLock(Object thread) {
480491 return lock ;
481492 }
482493
483- private String getThreadName (Object thread ) {
494+ String getThreadName (Object thread ) {
484495 return getContext ().getThreadName (thread );
485496 }
486497
@@ -631,19 +642,18 @@ public void suspend(Object thread, byte suspendPolicy, List<Callable<Void>> jobs
631642 case SuspendStrategy .ALL :
632643 fine (() -> "Suspend ALL" );
633644
634- Thread suspendThread = new Thread (new Runnable () {
635- @ Override
636- public void run () {
637- // suspend other threads
638- for (Object activeThread : getVisibleGuestThreads ()) {
639- if (activeThread != thread ) {
640- fine (() -> "Request thread suspend for other thread: " + getThreadName (activeThread ));
641- DebuggerController .this .suspend (activeThread );
642- }
645+ Thread suspendThread = new Thread (() -> {
646+ // suspend other threads
647+ for (Object activeThread : getVisibleGuestThreads ()) {
648+ if (activeThread != thread ) {
649+ fine (() -> "Request thread suspend for other thread: " + getThreadName (activeThread ));
650+ DebuggerController .this .suspend (activeThread );
643651 }
644652 }
645653 });
646654 suspendThread .start ();
655+ // pin all objects
656+ ids .pinAll ();
647657 suspendEventThread (thread , forceSuspend , jobs );
648658 break ;
649659 }
@@ -661,83 +671,60 @@ private static void runJobs(List<Callable<Void>> jobs) {
661671
662672 private void suspendEventThread (Object thread , boolean forceSuspend , List <Callable <Void >> jobs ) {
663673 fine (() -> "Suspending event thread: " + getThreadName (thread ) + " with new suspension count: " + threadSuspension .getSuspensionCount (thread ));
664- lockThread (thread , forceSuspend , true , jobs );
674+ lockThread (thread , forceSuspend , jobs );
665675 }
666676
667- private void lockThread (Object thread , boolean forceSuspend , boolean isFirstCall , List <Callable <Void >> jobs ) {
677+ private void lockThread (Object thread , boolean forceSuspend , List <Callable <Void >> jobs ) {
668678 SimpleLock lock = getSuspendLock (thread );
669- // in case a thread job is already posted on this thread
670- checkThreadJobsAndRun (thread , forceSuspend );
671679 synchronized (lock ) {
672680 if (!forceSuspend && !threadSuspension .isHardSuspended (thread )) {
673681 // thread was resumed from other command, so don't suspend now
674682 return ;
675683 }
684+
685+ if (lock .isLocked ()) {
686+ threadSuspension .suspendThread (thread );
687+ runJobs (jobs );
688+ }
689+ }
690+ while (!Thread .currentThread ().isInterrupted ()) {
676691 try {
677- if (lock .isLocked () && isFirstCall ) {
678- threadSuspension .suspendThread (thread );
679- runJobs (jobs );
680- }
681- while (lock .isLocked ()) {
682- fine (() -> "lock.wait() for thread: " + getThreadName (thread ));
692+ synchronized (lock ) {
693+ if (!lock .isLocked ()) {
694+ // released from other thread, so break loop
695+ break ;
696+ }
683697 // no reason to hold a hard suspension status, since now
684698 // we have the actual suspension status and suspended information
685699 threadSuspension .removeHardSuspendedThread (thread );
686- lock .wait ();
700+ fine (() -> "lock.wait() for thread: " + getThreadName (thread ));
701+ // Having the thread lock, we can check if an invoke job was posted outside of
702+ // locking, and if so, we postpone blocking the thread until next time around.
703+ if (!invokeJobs .containsKey (thread )) {
704+ lock .wait ();
705+ }
687706 }
688707 } catch (InterruptedException e ) {
689708 // the thread was interrupted, so let it run dry
690709 // make sure the interrupted flag is set though
691710 Thread .currentThread ().interrupt ();
692711 }
712+ checkInvokeJobsAndRun (thread );
693713 }
694-
695- checkThreadJobsAndRun (thread , forceSuspend );
696- getGCPrevention ().releaseActiveWhileSuspended (thread );
697714 fine (() -> "lock wakeup for thread: " + getThreadName (thread ));
698715 }
699716
700- private void checkThreadJobsAndRun (Object thread , boolean forceSuspend ) {
701- if (threadJobs .containsKey (thread )) {
702- // re-acquire the thread lock after completing
703- // the job, to avoid the thread resuming.
704- SimpleLock suspendLock = getSuspendLock (thread );
705- synchronized (suspendLock ) {
706- suspendLock .acquire ();
707- }
708- // a thread job was posted on this thread
709- // only wake up to perform the job a go back to sleep
710- ThreadJob <?> job = threadJobs .remove (thread );
711- byte suspensionStrategy = job .getSuspensionStrategy ();
712-
713- if (suspensionStrategy == SuspendStrategy .ALL ) {
714- Object [] allThreads = getVisibleGuestThreads ();
715- // resume all threads during invocation of method to avoid potential deadlocks
716- for (Object activeThread : allThreads ) {
717- if (activeThread != thread ) {
718- resume (activeThread );
719- }
720- }
721- // perform the job on this thread
722- job .runJob ();
723- // suspend all other threads after the invocation
724- for (Object activeThread : allThreads ) {
725- if (activeThread != thread ) {
726- suspend (activeThread );
727- }
728- }
729- } else {
730- job .runJob ();
731- }
732- lockThread (thread , forceSuspend , false , Collections .emptyList ());
717+ private void checkInvokeJobsAndRun (Object thread ) {
718+ if (invokeJobs .containsKey (thread )) {
719+ InvokeJob <?> job = invokeJobs .remove (thread );
720+ job .runJob (this );
733721 }
734722 }
735723
736- public void postJobForThread ( ThreadJob <?> job ) {
724+ public void postInvokeJobForThread ( InvokeJob <?> job ) {
737725 SimpleLock lock = getSuspendLock (job .getThread ());
738726 synchronized (lock ) {
739- threadJobs .put (job .getThread (), job );
740- lock .release ();
727+ invokeJobs .put (job .getThread (), job );
741728 lock .notifyAll ();
742729 }
743730 }
0 commit comments