< prev index next >

src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c

Print this page
*** 67,11 ***
      jthread thread;
      unsigned int toBeResumed : 1;      /* true if this thread was successfully suspended. */
      unsigned int pendingInterrupt : 1; /* true if thread is interrupted while handling an event. */
      unsigned int isDebugThread : 1;    /* true if this is one of our debug agent threads. */
      unsigned int suspendOnStart : 1;   /* true for new threads if we are currently in a VM.suspend(). */
!     unsigned int isStarted : 1;        /* THREAD_START or VIRTUAL_THREAD_SCHEDULED event received. */
      unsigned int popFrameEvent : 1;
      unsigned int popFrameProceed : 1;
      unsigned int popFrameThread : 1;
      EventIndex current_ei; /* Used to determine if we are currently handling an event on this thread. */
      jobject pendingStop;   /* Object we are throwing to stop the thread (ThreadReferenceImpl.stop). */
--- 67,12 ---
      jthread thread;
      unsigned int toBeResumed : 1;      /* true if this thread was successfully suspended. */
      unsigned int pendingInterrupt : 1; /* true if thread is interrupted while handling an event. */
      unsigned int isDebugThread : 1;    /* true if this is one of our debug agent threads. */
      unsigned int suspendOnStart : 1;   /* true for new threads if we are currently in a VM.suspend(). */
!     unsigned int isStarted : 1;        /* THREAD_START or VIRTUAL_THREAD_START event received. */
+     unsigned int is_vthread : 1;
      unsigned int popFrameEvent : 1;
      unsigned int popFrameProceed : 1;
      unsigned int popFrameThread : 1;
      EventIndex current_ei; /* Used to determine if we are currently handling an event on this thread. */
      jobject pendingStop;   /* Object we are throwing to stop the thread (ThreadReferenceImpl.stop). */

*** 122,10 ***
--- 123,11 ---
   * end events are maintained in the "runningThreads" list. All other threads known
   * to this module are kept in the "otherThreads" list.
   */
  static ThreadList runningThreads;
  static ThreadList otherThreads;
+ static ThreadList runningVThreads; /* VThreads we have seen. */
  
  #define MAX_DEBUG_THREADS 10
  static int debugThreadCount;
  static jthread debugThreads[MAX_DEBUG_THREADS];
  

*** 227,21 ***
      }
      return node;
  }
  
  /*
!  * These functions maintain the linked list of currently running threads.
   * All assume that the threadLock is held before calling.
   */
  
  /*
   * Search for a thread on the list. If list==NULL, search all lists.
   */
  static ThreadNode *
  findThread(ThreadList *list, jthread thread)
  {
      ThreadNode *node;
  
      /* Get thread local storage for quick thread -> node access */
      node = getThreadLocalStorage(thread);
  
      if ( node == NULL ) {
--- 229,22 ---
      }
      return node;
  }
  
  /*
!  * These functions maintain the linked list of currently running threads and vthreads.
   * All assume that the threadLock is held before calling.
   */
  
  /*
   * Search for a thread on the list. If list==NULL, search all lists.
   */
  static ThreadNode *
  findThread(ThreadList *list, jthread thread)
  {
      ThreadNode *node;
+     JNIEnv *env = getEnv();
  
      /* Get thread local storage for quick thread -> node access */
      node = getThreadLocalStorage(thread);
  
      if ( node == NULL ) {

*** 293,10 ***
--- 296,23 ---
          return NULL;
      }
      return node;
  }
  
+ /* Search for a running thread, including vthreads. */
+ static ThreadNode *
+ findRunningThread(jthread thread)
+ {
+     ThreadNode *node;
+     if (isVThread(thread)) {
+         node = findThread(&runningVThreads, thread);
+     } else {
+         node = findThread(&runningThreads, thread);
+     }
+     return node;
+ }
+ 
  /* Remove a ThreadNode from a ThreadList */
  static void
  removeNode(ThreadList *list, ThreadNode *node)
  {
      ThreadNode *prev;

*** 338,10 ***
--- 354,11 ---
  static ThreadNode *
  insertThread(JNIEnv *env, ThreadList *list, jthread thread)
  {
      ThreadNode *node;
      struct bag *eventBag;
+     jboolean is_vthread = (list == &runningVThreads);
  
      node = findThread(list, thread);
      if (node == NULL) {
          node = jvmtiAllocate(sizeof(*node));
          if (node == NULL) {

*** 365,25 ***
              jvmtiDeallocate(node);
              bagDestroyBag(eventBag);
              EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry");
              return NULL;
          }
!         /*
!          * Remember if it is a debug thread
!          */
!         if (threadControl_isDebugThread(node->thread)) {
!             node->isDebugThread = JNI_TRUE;
!         } else if (suspendAllCount > 0){
!             /*
!              * If there is a pending suspendAll, all new threads should
!              * be initialized as if they were suspended by the suspendAll,
!              * and the thread will need to be suspended when it starts.
!              */
!             node->suspendCount = suspendAllCount;
!             node->suspendOnStart = JNI_TRUE;
          }
          node->current_ei = 0;
          node->instructionStepMode = JVMTI_DISABLE;
          node->eventBag = eventBag;
          addNode(list, node);
  
  #ifdef DEBUG_THREADNAME
--- 382,49 ---
              jvmtiDeallocate(node);
              bagDestroyBag(eventBag);
              EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry");
              return NULL;
          }
!         if (!is_vthread) {
!             if (threadControl_isDebugThread(node->thread)) {
!                 /* Remember if it is a debug thread */
!                 node->isDebugThread = JNI_TRUE;
!             } else {
!                 if (suspendAllCount > 0) {
!                     /*
!                      * If there is a pending suspendAll, all new threads should
!                      * be initialized as if they were suspended by the suspendAll,
!                      * and the thread will need to be suspended when it starts.
!                      */
!                     node->suspendCount = suspendAllCount;
!                     node->suspendOnStart = JNI_TRUE;
+                 }
+             }
+         } else { /* vthread */
+             jint vthread_state = 0;
+             jvmtiError error = threadState(node->thread, &vthread_state);
+             if (error != JVMTI_ERROR_NONE) {
+                 EXIT_ERROR(error, "getting thread state");
+             }
+             if (suspendAllCount > 0) {
+                 // Assume the suspendAllCount, just like the regular thread case above.
+                 node->suspendCount = suspendAllCount;
+                 if (vthread_state == 0) {
+                     // If state == 0, then this is a new vthread that has not been started yet.
+                     // Need to suspendOnStart in that case, just like the regular thread case above.
+                     node->suspendOnStart = JNI_TRUE;
+                     list = &otherThreads; // Put on otherThreads list instead of runningVThreads
+                 }
+             }
+             if (vthread_state != 0) {
+                 // This is an already started vthread that we were not already tracking.
+                 node->isStarted = JNI_TRUE;
+             }
          }
+ 
          node->current_ei = 0;
+         node->is_vthread = is_vthread;
          node->instructionStepMode = JVMTI_DISABLE;
          node->eventBag = eventBag;
          addNode(list, node);
  
  #ifdef DEBUG_THREADNAME

*** 462,10 ***
--- 503,14 ---
  
  static void
  moveNode(ThreadList *source, ThreadList *dest, ThreadNode *node)
  {
      removeNode(source, node);
+     // vthread fixme: we should really fix the caller to pass the right list
+     if (node->is_vthread && dest == &runningThreads) {
+         dest = &runningVThreads;
+     }
      JDI_ASSERT(findThread(dest, node->thread) == NULL);
      addNode(dest, node);
  }
  
  typedef jvmtiError (*ThreadEnumerateFunction)(JNIEnv *, ThreadNode *, void *);

*** 625,10 ***
--- 670,11 ---
      jvmtiError error;
  
      suspendAllCount = 0;
      runningThreads.first = NULL;
      otherThreads.first = NULL;
+     runningVThreads.first = NULL;
      debugThreadCount = 0;
      threadLock = debugMonitorCreate("JDWP Thread Lock");
      if (gdata->threadClass==NULL) {
          EXIT_ERROR(AGENT_ERROR_NULL_POINTER, "no java.lang.thread class");
      }

*** 683,10 ***
--- 729,15 ---
                      return JNI_TRUE;
                  }
              }
          }
          node = node->next;
+         if (node == NULL && list == &runningThreads) {
+             // We need to look at runningVThreads after we are done with runningThreads.
+             list = &runningVThreads;
+             node = list->first;
+         }
      }
      return JNI_FALSE;
  }
  
  static void

*** 719,11 ***
  
      thread = evinfo->thread;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          if (node->resumeFrameDepth > 0) {
              jint compareDepth = getStackDepth(thread);
              if (evinfo->ei == EI_FRAME_POP) {
                  compareDepth--;
--- 770,11 ---
  
      thread = evinfo->thread;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL) {
          if (node->resumeFrameDepth > 0) {
              jint compareDepth = getStackDepth(thread);
              if (evinfo->ei == EI_FRAME_POP) {
                  compareDepth--;

*** 758,11 ***
      jvmtiError  error;
      FrameNumber fnum;
      ThreadNode *node;
  
      fnum = 0;
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          JDI_ASSERT(node->resumeFrameDepth == 0);
          error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
                          (gdata->jvmti, thread, fnum);
          if (error == JVMTI_ERROR_NONE) {
--- 809,11 ---
      jvmtiError  error;
      FrameNumber fnum;
      ThreadNode *node;
  
      fnum = 0;
!     node = findRunningThread(thread);
      if (node != NULL) {
          JDI_ASSERT(node->resumeFrameDepth == 0);
          error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
                          (gdata->jvmti, thread, fnum);
          if (error == JVMTI_ERROR_NONE) {

*** 1046,10 ***
--- 1097,11 ---
                   * failure to resume the thread.
                   */
                  error = JVMTI_ERROR_NONE;
              }
          }
+         // vthread fixme: If this is a vthread and suspendCount == 0, we should delete the node.
      }
  
      return error;
  }
  

*** 1110,22 ***
  static jvmtiError
  commonSuspend(JNIEnv *env, jthread thread, jboolean deferred)
  {
      ThreadNode *node;
  
!     /*
!      * If the thread is not between its start and end events, we should
!      * still suspend it. To keep track of things, add the thread
!      * to a separate list of threads so that we'll resume it later.
!      */
!     node = findThread(&runningThreads, thread);
  #if 0
      tty_message("commonSuspend: node(%p) suspendCount(%d) %s", node, node->suspendCount, node->name);
  #endif
-     if (node == NULL) {
-         node = insertThread(env, &otherThreads, thread);
-     }
  
      if ( deferred ) {
          return deferredSuspendThreadByNode(node);
      } else {
          return suspendThreadByNode(node);
--- 1162,32 ---
  static jvmtiError
  commonSuspend(JNIEnv *env, jthread thread, jboolean deferred)
  {
      ThreadNode *node;
  
!     node = findRunningThread(thread);
! 
!     if (node == NULL) {
!         if (isVThread(thread)) {
!             /*
!              * Since we don't track all vthreads, it might not be in the list already. Start
+              * tracking it now.
+              */
+             node = insertThread(env, &runningVThreads, thread);
+         } else {
+             /*
+              * If the thread is not between its start and end events, we should
+              * still suspend it. To keep track of things, add the thread
+              * to a separate list of threads so that we'll resume it later.
+              */
+             node = insertThread(env, &otherThreads, thread);
+         }
+     }
+ 
  #if 0
      tty_message("commonSuspend: node(%p) suspendCount(%d) %s", node, node->suspendCount, node->name);
  #endif
  
      if ( deferred ) {
          return deferredSuspendThreadByNode(node);
      } else {
          return suspendThreadByNode(node);

*** 1162,10 ***
--- 1224,11 ---
       * the thread because suspendCount will be 1 meaning that the
       * debugger has the thread suspended.  See bug 6224859.
       */
      if (node->suspendCount == 1 && (!node->toBeResumed || node->suspendOnStart)) {
          node->suspendCount--;
+         // vthread fixme: If this is a vthread, we should delete the node.
          return JVMTI_ERROR_NONE;
      }
  
      if (arg == NULL) {
          /* nothing to hard resume so we're done */

*** 1263,14 ***
--- 1326,18 ---
      reqCnt = 0;
  
      /* count number of threads to hard resume */
      (void) enumerateOverThreadList(env, &runningThreads, resumeCountHelper,
                                     &reqCnt);
+     (void) enumerateOverThreadList(env, &runningVThreads, resumeCountHelper,
+                                    &reqCnt);
      if (reqCnt == 0) {
          /* nothing to hard resume so do just the accounting part */
          (void) enumerateOverThreadList(env, &runningThreads, resumeCopyHelper,
                                         NULL);
+         (void) enumerateOverThreadList(env, &runningVThreads, resumeCopyHelper,
+                                        NULL);
          return JVMTI_ERROR_NONE;
      }
  
      /*LINTED*/
      reqList = newArray(reqCnt, sizeof(jthread));

*** 1285,17 ***
  
      /* copy the jthread values for threads to hard resume */
      reqPtr = reqList;
      (void) enumerateOverThreadList(env, &runningThreads, resumeCopyHelper,
                                     &reqPtr);
  
      error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThreadList)
                  (gdata->jvmti, reqCnt, reqList, results);
      for (i = 0; i < reqCnt; i++) {
          ThreadNode *node;
  
!         node = findThread(&runningThreads, reqList[i]);
          if (node == NULL) {
              EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in running thread table");
          }
          LOG_MISC(("thread=%p resumed as part of list", node->thread));
  
--- 1352,19 ---
  
      /* copy the jthread values for threads to hard resume */
      reqPtr = reqList;
      (void) enumerateOverThreadList(env, &runningThreads, resumeCopyHelper,
                                     &reqPtr);
+     (void) enumerateOverThreadList(env, &runningVThreads, resumeCopyHelper,
+                                    &reqPtr);
  
      error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThreadList)
                  (gdata->jvmti, reqCnt, reqList, results);
      for (i = 0; i < reqCnt; i++) {
          ThreadNode *node;
  
!         node = findRunningThread(reqList[i]);
          if (node == NULL) {
              EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in running thread table");
          }
          LOG_MISC(("thread=%p resumed as part of list", node->thread));
  

*** 1305,10 ***
--- 1374,12 ---
           * the same here. We also don't clear the error.
           */
          node->suspendCount--;
          node->toBeResumed = JNI_FALSE;
          node->frameGeneration++; /* Increment on each resume */
+ 
+         // vthread fixme: If this is a vthread, we should delete the node.
      }
      deleteArray(results);
      deleteArray(reqList);
  
      debugMonitorNotifyAll(threadLock);

*** 1500,14 ***
  jvmtiError
  threadControl_suspendCount(jthread thread, jint *count)
  {
      jvmtiError  error;
      ThreadNode *node;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node == NULL) {
          node = findThread(&otherThreads, thread);
      }
  
      error = JVMTI_ERROR_NONE;
--- 1571,19 ---
  jvmtiError
  threadControl_suspendCount(jthread thread, jint *count)
  {
      jvmtiError  error;
      ThreadNode *node;
+     jboolean is_vthread = isVThread(thread);
  
      debugMonitorEnter(threadLock);
  
!     if (is_vthread) {
+         node = findThread(&runningVThreads, thread);
+     } else {
+         node = findThread(&runningThreads, thread);
+     }
      if (node == NULL) {
          node = findThread(&otherThreads, thread);
      }
  
      error = JVMTI_ERROR_NONE;

*** 1516,11 ***
--- 1592,26 ---
      } else {
          /*
           * If the node is in neither list, the debugger never suspended
           * this thread, so the suspend count is 0.
           */
+       if (is_vthread) {
+           jint vthread_state = 0;
+           jvmtiError error = threadState(thread, &vthread_state);
+           if (error != JVMTI_ERROR_NONE) {
+               EXIT_ERROR(error, "getting thread state");
+           }
+           if (vthread_state == 0) {
+               // If state == 0, then this is a new vthread that has not been started yet.
+               *count = 0;
+           } else {
+               // This is a started vthread that we are not tracking. Use suspendAllCount.
+               *count = suspendAllCount;
+           }
+       } else {
          *count = 0;
+       }
      }
  
      debugMonitorExit(threadLock);
  
      return error;

*** 1538,10 ***
--- 1629,18 ---
      }
      return JNI_FALSE;
  }
  
  
+ static jvmtiError
+ incrementSuspendCountHelper(JNIEnv *env, ThreadNode *node, void *arg)
+ {
+     node->toBeResumed = JNI_TRUE;
+     node->suspendCount++;
+     return JVMTI_ERROR_NONE;
+ }
+ 
  typedef struct {
      jthread *list;
      jint count;
  } SuspendAllArg;
  

*** 1579,10 ***
--- 1678,33 ---
      WITH_LOCAL_REFS(env, 1) {
  
          jthread *threads;
          jint count;
  
+         if (gdata->vthreadsSupported) {
+             /* Tell JVMTI to suspend all virtual threads. */
+             if (suspendAllCount == 0) {
+                 error = JVMTI_FUNC_PTR(gdata->jvmti, SuspendAllVirtualThreads)
+                         (gdata->jvmti, 0, NULL);
+                 if (error != JVMTI_ERROR_NONE) {
+                     EXIT_ERROR(error, "cannot suspend all virtual threads");
+                 }
+                 // We need a notify here just like we do any time we suspend a thread.
+                 // See commonSuspendList() and suspendThreadByNode().
+                 debugMonitorNotifyAll(threadLock);
+             }
+ 
+             /*
+              * Increment suspendCount of each virtual thread that we are tracking. Note the
+              * compliment to this that happens during the resumeAll() is handled by
+              * commonResumeList(), so it's a bit orthogonal to how we handle incrementing
+              * the suspendCount.
+              */
+             error = enumerateOverThreadList(env, &runningVThreads, incrementSuspendCountHelper, NULL);
+             JDI_ASSERT(error == JVMTI_ERROR_NONE);
+         }
+ 
          threads = allThreads(&count);
          if (threads == NULL) {
              error = AGENT_ERROR_OUT_OF_MEMORY;
              goto err;
          }

*** 1632,10 ***
--- 1754,33 ---
       * of the two thread lists.
       */
      return resumeThreadByNode(node);
  }
  
+ static jvmtiError
+ excludeCountHelper(JNIEnv *env, ThreadNode *node, void *arg)
+ {
+     JDI_ASSERT(node->is_vthread);
+     if (node->suspendCount > 0) {
+         jint *counter = (jint *)arg;
+         (*counter)++;
+     }
+     return JVMTI_ERROR_NONE;
+ }
+ 
+ static jvmtiError
+ excludeCopyHelper(JNIEnv *env, ThreadNode *node, void *arg)
+ {
+     JDI_ASSERT(node->is_vthread);
+     if (node->suspendCount > 0) {
+         jthread **listPtr = (jthread **)arg;
+         **listPtr = node->thread;
+         (*listPtr)++;
+     }
+     return JVMTI_ERROR_NONE;
+ }
+ 
  jvmtiError
  threadControl_resumeAll(void)
  {
      jvmtiError error;
      JNIEnv    *env;

*** 1648,10 ***
--- 1793,46 ---
      log_debugee_location("threadControl_resumeAll()", NULL, NULL, 0);
  
      eventHandler_lock(); /* for proper lock order */
      debugMonitorEnter(threadLock);
  
+     if (gdata->vthreadsSupported) {
+         if (suspendAllCount == 1) {
+             jint excludeCnt = 0;
+             jthread *excludeList = NULL;
+             /*
+              * Tell JVMTI to resume all virtual threads except for those we
+              * are tracking separately. The commonResumeList() call below will
+              * resume any vthread with a suspendCount == 1, and we want to ignore
+              * vthreads with a suspendCount > 0. Therefor we don't want
+              * ResumeAllVirtualThreads resuming these vthreads. We must first
+              * build a list of them to pass to as the exclude list.
+              */
+             enumerateOverThreadList(env, &runningVThreads, excludeCountHelper,
+                                     &excludeCnt);
+             if (excludeCnt > 0) {
+                 excludeList = newArray(excludeCnt, sizeof(jthread));
+                 if (excludeList == NULL) {
+                     EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"exclude list");
+                 }
+                 {
+                     jthread *excludeListPtr = excludeList;
+                     enumerateOverThreadList(env, &runningVThreads, excludeCopyHelper,
+                                             &excludeListPtr);
+                 }
+             }
+             error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeAllVirtualThreads)
+                     (gdata->jvmti, excludeCnt, excludeList);
+             if (error != JVMTI_ERROR_NONE) {
+                 EXIT_ERROR(error, "cannot resume all virtual threads");
+             }
+             // We need a notify here just like we do any time we resume a thread.
+             // See commonResumeList() and resumeThreadByNode().
+             debugMonitorNotifyAll(threadLock);
+         }
+     }
+ 
      /*
       * Resume only those threads that the debugger has suspended. All
       * such threads must have a node in one of the thread lists, so there's
       * no need to get the whole thread list from JVMTI (unlike
       * suspendAll).

*** 1689,11 ***
  
      step = NULL;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          step = &node->currentStep;
      }
  
      debugMonitorExit(threadLock);
--- 1870,11 ---
  
      step = NULL;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL) {
          step = &node->currentStep;
      }
  
      debugMonitorExit(threadLock);

*** 1709,11 ***
  
      request = NULL;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
           request = &node->currentInvoke;
      }
  
      debugMonitorExit(threadLock);
--- 1890,11 ---
  
      request = NULL;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL) {
           request = &node->currentInvoke;
      }
  
      debugMonitorExit(threadLock);

*** 2075,10 ***
--- 2256,14 ---
              case EI_THREAD_END:
                  /* Thread wants to end? let it. */
                  setPopFrameThread(thread, JNI_FALSE);
                  popFrameCompleteEvent(thread);
                  break;
+             case EI_VIRTUAL_THREAD_START:
+             case EI_VIRTUAL_THREAD_END:
+                 JDI_ASSERT(JNI_FALSE);
+                 break;
              case EI_SINGLE_STEP:
                  /* This is an event we requested to mark the */
                  /*        completion of the pop frame */
                  popFrameCompleteEvent(thread);
                  return JNI_TRUE;

*** 2147,14 ***
           * the thread node may need to be created.
           *
           * It is possible for certain events (notably method entry/exit)
           * to precede thread start for some VM implementations.
           */
!         node = insertThread(env, &runningThreads, thread);
      }
  
!     if (ei == EI_THREAD_START) {
          node->isStarted = JNI_TRUE;
          processDeferredEventModes(env, thread, node);
      }
  
      node->current_ei = ei;
--- 2332,22 ---
           * the thread node may need to be created.
           *
           * It is possible for certain events (notably method entry/exit)
           * to precede thread start for some VM implementations.
           */
!         if (evinfo->is_vthread) {
+           /* fiber fixme: don't add the vthread if this is an EI_THREAD_START or
+              EI_THREAD_EXIT event. Otherwise we end up adding every vthread. This
+              is an issue when notifyVThreads is true, which is the default.
+           */
+             node = insertThread(env, &runningVThreads, thread);
+         } else {
+             node = insertThread(env, &runningThreads, thread);
+         }
      }
  
!     if (ei == EI_THREAD_START || ei == EI_VIRTUAL_THREAD_START) {
          node->isStarted = JNI_TRUE;
          processDeferredEventModes(env, thread, node);
      }
  
      node->current_ei = ei;

*** 2213,20 ***
      if (ei == EI_THREAD_END) {
          eventHandler_lock(); /* for proper lock order */
      }
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node == NULL) {
          EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"thread list corrupted");
      } else {
          JNIEnv *env;
  
          env = getEnv();
          if (ei == EI_THREAD_END) {
              jboolean inResume = (node->resumeFrameDepth > 0);
!             removeThread(env, &runningThreads, thread);
              node = NULL;   /* has been freed */
  
              /*
               * Clean up mechanism used to detect end of
               * resume.
--- 2406,24 ---
      if (ei == EI_THREAD_END) {
          eventHandler_lock(); /* for proper lock order */
      }
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node == NULL) {
          EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"thread list corrupted");
      } else {
          JNIEnv *env;
  
          env = getEnv();
          if (ei == EI_THREAD_END) {
              jboolean inResume = (node->resumeFrameDepth > 0);
!             if (isVThread(thread)) {
+                 removeThread(env, &runningVThreads, thread);
+             } else {
+                 removeThread(env, &runningThreads, thread);
+             }
              node = NULL;   /* has been freed */
  
              /*
               * Clean up mechanism used to detect end of
               * resume.

*** 2264,11 ***
      error = threadState(thread, &state);
      *pstatus = map2jdwpThreadStatus(state);
      *statusFlags = map2jdwpSuspendStatus(state);
  
      if (error == JVMTI_ERROR_NONE) {
!         node = findThread(&runningThreads, thread);
          if ((node != NULL) && HANDLING_EVENT(node)) {
              /*
               * While processing an event, an application thread is always
               * considered to be running even if its handler happens to be
               * cond waiting on an internal debugger monitor, etc.
--- 2461,11 ---
      error = threadState(thread, &state);
      *pstatus = map2jdwpThreadStatus(state);
      *statusFlags = map2jdwpSuspendStatus(state);
  
      if (error == JVMTI_ERROR_NONE) {
!         node = findRunningThread(thread);
          if ((node != NULL) && HANDLING_EVENT(node)) {
              /*
               * While processing an event, an application thread is always
               * considered to be running even if its handler happens to be
               * cond waiting on an internal debugger monitor, etc.

*** 2277,10 ***
--- 2474,15 ---
               * to distinguish debugger suspends from app suspends.
               */
              *pstatus = JDWP_THREAD_STATUS(RUNNING);
          }
      }
+ #if 0
+     tty_message("status %s: node(%p) suspendCount(%d) %d %d %s",
+                 isVThread(thread) ? "vthread" : "thread",
+                 node, node->suspendCount, *pstatus, *statusFlags, node->name);
+ #endif
  
      debugMonitorExit(threadLock);
  
      return error;
  }

*** 2289,10 ***
--- 2491,13 ---
  threadControl_interrupt(jthread thread)
  {
      ThreadNode *node;
      jvmtiError  error;
  
+     // vthread fixme: should this work for vthreads?
+     JDI_ASSERT(!isVThread(thread));
+ 
      error = JVMTI_ERROR_NONE;
  
      log_debugee_location("threadControl_interrupt()", thread, NULL, 0);
  
      debugMonitorEnter(threadLock);

*** 2318,11 ***
  {
      ThreadNode *node;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          node->cleInfo.ei = 0;
          if (node->cleInfo.clazz != NULL) {
              tossGlobalRef(env, &(node->cleInfo.clazz));
          }
--- 2523,11 ---
  {
      ThreadNode *node;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL) {
          node->cleInfo.ei = 0;
          if (node->cleInfo.clazz != NULL) {
              tossGlobalRef(env, &(node->cleInfo.clazz));
          }

*** 2340,11 ***
  
      result = JNI_FALSE;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL && node->cleInfo.ei != 0 &&
          node->cleInfo.method == method &&
          node->cleInfo.location == location &&
          (isSameObject(env, node->cleInfo.clazz, clazz))) {
          result = JNI_TRUE; /* we have a match */
--- 2545,11 ---
  
      result = JNI_FALSE;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL && node->cleInfo.ei != 0 &&
          node->cleInfo.method == method &&
          node->cleInfo.location == location &&
          (isSameObject(env, node->cleInfo.clazz, clazz))) {
          result = JNI_TRUE; /* we have a match */

*** 2361,11 ***
  {
      ThreadNode *node;
  
      debugMonitorEnter(threadLock);
  
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          node->cleInfo.ei = ei;
          /* Create a class ref that will live beyond */
          /* the end of this call */
          saveGlobalRef(env, clazz, &(node->cleInfo.clazz));
--- 2566,11 ---
  {
      ThreadNode *node;
  
      debugMonitorEnter(threadLock);
  
!     node = findRunningThread(thread);
      if (node != NULL) {
          node->cleInfo.ei = ei;
          /* Create a class ref that will live beyond */
          /* the end of this call */
          saveGlobalRef(env, clazz, &(node->cleInfo.clazz));

*** 2380,10 ***
--- 2585,13 ---
  void
  threadControl_setPendingInterrupt(jthread thread)
  {
      ThreadNode *node;
  
+     // vthread fixme: should this work for vthreads?
+     JDI_ASSERT(!isVThread(thread));
+ 
      debugMonitorEnter(threadLock);
  
      node = findThread(&runningThreads, thread);
      if (node != NULL) {
          node->pendingInterrupt = JNI_TRUE;

*** 2396,10 ***
--- 2604,13 ---
  threadControl_stop(jthread thread, jobject throwable)
  {
      ThreadNode *node;
      jvmtiError  error;
  
+     // vthread fixme: should this work for vthreads?
+     JDI_ASSERT(!isVThread(thread));
+ 
      error = JVMTI_ERROR_NONE;
  
      log_debugee_location("threadControl_stop()", thread, NULL, 0);
  
      debugMonitorEnter(threadLock);

*** 2465,12 ***
--- 2676,25 ---
      JNIEnv *env;
  
      env = getEnv();
      eventHandler_lock(); /* for proper lock order */
      debugMonitorEnter(threadLock);
+ 
+     if (gdata->vthreadsSupported) {
+         if (suspendAllCount > 0) {
+             /* Tell JVMTI to resume all virtual threads. */
+             jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeAllVirtualThreads)
+               (gdata->jvmti, 0, NULL);
+             if (error != JVMTI_ERROR_NONE) {
+                 EXIT_ERROR(error, "cannot resume all virtual threads");
+             }
+         }
+     }
+ 
      (void)enumerateOverThreadList(env, &runningThreads, resetHelper, NULL);
      (void)enumerateOverThreadList(env, &otherThreads, resetHelper, NULL);
+     (void)enumerateOverThreadList(env, &runningVThreads, resetHelper, NULL);
  
      removeResumed(env, &otherThreads);
  
      freeDeferredEventModes(env);
  

*** 2490,11 ***
      jvmtiEventMode mode;
  
      mode = JVMTI_DISABLE;
  
      debugMonitorEnter(threadLock);
!     node = findThread(&runningThreads, thread);
      if (node != NULL) {
          mode = node->instructionStepMode;
      }
      debugMonitorExit(threadLock);
      return mode;
--- 2714,11 ---
      jvmtiEventMode mode;
  
      mode = JVMTI_DISABLE;
  
      debugMonitorEnter(threadLock);
!     node = findRunningThread(thread);
      if (node != NULL) {
          mode = node->instructionStepMode;
      }
      debugMonitorExit(threadLock);
      return mode;

*** 2513,11 ***
          /* Thread event */
          ThreadNode *node;
  
          debugMonitorEnter(threadLock);
          {
!             node = findThread(&runningThreads, thread);
              if ((node == NULL) || (!node->isStarted)) {
                  JNIEnv *env;
  
                  env = getEnv();
                  error = addDeferredEventMode(env, mode, ei, thread);
--- 2737,11 ---
          /* Thread event */
          ThreadNode *node;
  
          debugMonitorEnter(threadLock);
          {
!             node = findRunningThread(thread);
              if ((node == NULL) || (!node->isStarted)) {
                  JNIEnv *env;
  
                  env = getEnv();
                  error = addDeferredEventMode(env, mode, ei, thread);

*** 2571,20 ***
      debugMonitorExit(threadLock);
  
      return frameGeneration;
  }
  
  /***** debugging *****/
  
  #ifdef DEBUG
  
  void
  threadControl_dumpAllThreads()
  {
!     tty_message("Dumping runningThreads:\n");
      dumpThreadList(&runningThreads);
!     tty_message("Dumping otherThreads:\n");
      dumpThreadList(&otherThreads);
  }
  
  void
  threadControl_dumpThread(jthread thread)
--- 2795,70 ---
      debugMonitorExit(threadLock);
  
      return frameGeneration;
  }
  
+ jthread *
+ threadControl_allVThreads(jint *numVThreads)
+ {
+     JNIEnv *env;
+     ThreadNode *node;
+     jthread* vthreads;
+ 
+     env = getEnv();
+     debugMonitorEnter(threadLock);
+ 
+     /* Count the number of vthreads */
+     /* vthread fixme: we should keep a running total so no counting is needed. */
+     *numVThreads = 0;
+     for (node = runningVThreads.first; node != NULL; node = node->next) {
+         (*numVThreads)++;
+     }
+ 
+     /* Allocate and fill in the vthreads array. */
+     vthreads = jvmtiAllocate(*numVThreads * sizeof(jthread*));
+     if (vthreads != NULL) {
+         int i = 0;
+         for (node = runningVThreads.first; node != NULL;  node = node->next) {
+             vthreads[i++] = node->thread;
+         }
+     }
+ 
+     debugMonitorExit(threadLock);
+ 
+     return vthreads;
+ }
+ 
+ jboolean threadControl_isKnownVThread(jthread vthread) {
+     ThreadNode *vthreadNode;
+     debugMonitorEnter(threadLock);
+     vthreadNode = findThread(&runningVThreads, vthread);
+     debugMonitorExit(threadLock);
+     return vthreadNode != NULL;
+ }
+ 
+ void
+ threadControl_addVThread(jthread vthread)
+ {
+     ThreadNode *vthreadNode;
+     debugMonitorEnter(threadLock);
+     vthreadNode = insertThread(getEnv(), &runningVThreads, vthread);
+     debugMonitorExit(threadLock);
+ }
+ 
  /***** debugging *****/
  
  #ifdef DEBUG
  
  void
  threadControl_dumpAllThreads()
  {
!     tty_message("Dumping runningThreads:");
      dumpThreadList(&runningThreads);
!     tty_message("\nDumping runningVThreads:");
+     dumpThreadList(&runningVThreads);
+     tty_message("\nDumping otherThreads:");
      dumpThreadList(&otherThreads);
  }
  
  void
  threadControl_dumpThread(jthread thread)
< prev index next >