< prev index next >

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

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1998, 2007, 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
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -37,10 +37,12 @@
 /*
  * Collection of info for properly handling co-located events.
  * If the ei field is non-zero, then one of the possible
  * co-located events has been posted and the other fields describe
  * the event's location.
+ *
+ * See comment above deferEventReport() for an explanation of co-located events.
  */
 typedef struct CoLocatedEventInfo_ {
     EventIndex ei;
     jclass    clazz;
     jmethodID method;

@@ -61,35 +63,47 @@
  * from the debugger. suspends from the app itself are
  * not included in this count.
  */
 typedef struct ThreadNode {
     jthread thread;
-    unsigned int toBeResumed : 1;
-    unsigned int pendingInterrupt : 1;
-    unsigned int isDebugThread : 1;
-    unsigned int suspendOnStart : 1;
-    unsigned int isStarted : 1;
+    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 FIBER_SCHEDULED event received. */
+    unsigned int is_fiber : 1;
     unsigned int popFrameEvent : 1;
     unsigned int popFrameProceed : 1;
     unsigned int popFrameThread : 1;
-    EventIndex current_ei;
-    jobject pendingStop;
+    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). */
     jint suspendCount;
     jint resumeFrameDepth; /* !=0 => This thread is in a call to Thread.resume() */
     jvmtiEventMode instructionStepMode;
     StepRequest currentStep;
     InvokeRequest currentInvoke;
-    struct bag *eventBag;
-    CoLocatedEventInfo cleInfo;
+    struct bag *eventBag;       /* Accumulation of JDWP events to be sent as a reply. */
+    CoLocatedEventInfo cleInfo; /* See comment above deferEventReport() for an explanation. */
+    jthread fiberHelperThread;  /* Temporary thread created for mounting fiber on to get stack trace
+                                 * or to support suspending an unmounted fiber. */
+    jboolean isTrackedSuspendedFiber; /* true if we are tracking the suspendCount of this fiber. */
+    struct ThreadNode *nextTrackedSuspendedFiber;
+    struct ThreadNode *prevTrackedSuspendedFiber;
     struct ThreadNode *next;
     struct ThreadNode *prev;
-    jlong frameGeneration;
-    struct ThreadList *list;  /* Tells us what list this thread is in */
+    jlong frameGeneration;    /* used to generate a unique frameID. Incremented whenever existing frameID
+                                 needs to be invalidated, such as when the thread is resumed. */
+    struct ThreadList *list;  /* Tells us what list this thread is in. */
+#ifdef DEBUG_THREADNAME
+    char name[256];
+#endif
 } ThreadNode;
 
 static jint suspendAllCount;
 
+struct ThreadNode *trackedSuspendedFibers = NULL;
+
 typedef struct ThreadList {
     ThreadNode *first;
 } ThreadList;
 
 /*

@@ -116,10 +130,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 runningFibers; /* Fibers we have seen. */
 
 #define MAX_DEBUG_THREADS 10
 static int debugThreadCount;
 static jthread debugThreads[MAX_DEBUG_THREADS];
 

@@ -216,29 +231,44 @@
     }
     return node;
 }
 
 /*
- * These functions maintain the linked list of currently running threads.
+ * These functions maintain the linked list of currently running threads and fibers.
  * All assume that the threadLock is held before calling.
- * If list==NULL, search both lists.
+ */
+
+
+/*
+ * 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();
+
+    if (list == NULL || list == &runningFibers) {
+        /*
+         * Search for a fiber.
+         * fiber fixme: this needs to be done a lot faster. Maybe some sort of TLS for fibers is needed.
+         * Otherwise we'll need something like a hashlist front end to the runningFibers list so
+         * we can do quick lookups.
+         */
+        ThreadNode *node = nonTlsSearch(env, &runningFibers, thread);
+        if (node != NULL || list == &runningFibers) {
+            return node;
+        }
+    }    
 
     /* Get thread local storage for quick thread -> node access */
     node = getThreadLocalStorage(thread);
 
     /* In some rare cases we might get NULL, so we check the list manually for
      *   any threads that we could match.
      */
     if ( node == NULL ) {
-        JNIEnv *env;
-
-        env = getEnv();
         if ( list != NULL ) {
             node = nonTlsSearch(env, list, thread);
         } else {
             node = nonTlsSearch(env, &runningThreads, thread);
             if ( node == NULL ) {

@@ -301,10 +331,11 @@
 static ThreadNode *
 insertThread(JNIEnv *env, ThreadList *list, jthread thread)
 {
     ThreadNode *node;
     struct bag *eventBag;
+    jboolean is_fiber = (list == &runningFibers);
 
     node = findThread(list, thread);
     if (node == NULL) {
         node = jvmtiAllocate(sizeof(*node));
         if (node == NULL) {

@@ -331,11 +362,11 @@
             return NULL;
         }
         /*
          * Remember if it is a debug thread
          */
-        if (threadControl_isDebugThread(node->thread)) {
+        if (!is_fiber && 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,

@@ -343,19 +374,26 @@
              */
             node->suspendCount = suspendAllCount;
             node->suspendOnStart = JNI_TRUE;
         }
         node->current_ei = 0;
+        node->is_fiber = is_fiber;
         node->instructionStepMode = JVMTI_DISABLE;
         node->eventBag = eventBag;
         addNode(list, node);
 
         /* Set thread local storage for quick thread -> node access.
          *   Some threads may not be in a state that allows setting of TLS,
          *   which is ok, see findThread, it deals with threads without TLS set.
          */
-        setThreadLocalStorage(node->thread, (void*)node);
+        if (!is_fiber) {
+            setThreadLocalStorage(node->thread, (void*)node);
+        }
+
+        if (is_fiber) {
+            node->isStarted = JNI_TRUE; /* Fibers are considered started by default. */
+        }
     }
 
     return node;
 }
 

@@ -368,11 +406,13 @@
     stepControl_clearRequest(node->thread, &node->currentStep);
     if (node->isDebugThread) {
         (void)threadControl_removeDebugThread(node->thread);
     }
     /* Clear out TLS on this thread (just a cleanup action) */
-    setThreadLocalStorage(node->thread, NULL);
+    if (!node->is_fiber) {
+        setThreadLocalStorage(node->thread, NULL);
+    }
     tossGlobalRef(env, &(node->thread));
     bagDestroyBag(node->eventBag);
     jvmtiDeallocate(node);
 }
 

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

@@ -654,10 +695,13 @@
                           struct bag *eventBag)
 {
     ThreadNode *node;
     jthread     thread;
 
+    /* fiber fixme: it's unclear how this is used and if anything special needs to be done for fibers. */
+    JDI_ASSERT(!evinfo->matchesFiber);
+
     thread = evinfo->thread;
 
     debugMonitorEnter(threadLock);
 
     node = findThread(&runningThreads, thread);

@@ -735,10 +779,13 @@
 static void
 handleAppResumeBreakpoint(JNIEnv *env, EventInfo *evinfo,
                           HandlerNode *handlerNode,
                           struct bag *eventBag)
 {
+    /* fiber fixme: it's unclear how this is used and if anything special needs to be done for fibers. */
+    JDI_ASSERT(!evinfo->matchesFiber);
+
     jthread resumer = evinfo->thread;
     jthread resumee = getResumee(resumer);
 
     debugMonitorEnter(threadLock);
     if (resumee != NULL) {

@@ -841,10 +888,114 @@
     } END_WITH_LOCAL_REFS(env)
 
     debugMonitorExit(threadLock);
 }
 
+
+static jvmtiError
+resumeFiberHelperThread(JNIEnv *env, ThreadNode *node, void *ignored)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    if (node->fiberHelperThread != NULL) {
+        error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThread)
+            (gdata->jvmti, node->fiberHelperThread);
+        tossGlobalRef(env, &node->fiberHelperThread);
+    }
+    return error;
+}
+
+static void
+startTrackingSuspendedFiber(ThreadNode *fiberNode)
+{
+    /* Add fiberNode to the start of the list. */
+    fiberNode->prevTrackedSuspendedFiber = NULL;
+    fiberNode->nextTrackedSuspendedFiber = trackedSuspendedFibers;
+    trackedSuspendedFibers = fiberNode;
+
+    /* Since we didn't previously increment suspendCount for each suspendAll(), do that now. */
+    fiberNode->suspendCount = suspendAllCount;
+
+    fiberNode->isTrackedSuspendedFiber = JNI_TRUE;
+}
+
+
+static void
+stopTrackingSuspendedFiber(ThreadNode *fiberNode)
+{
+    /* Remove fiberNode from the list. */
+    if (fiberNode->prevTrackedSuspendedFiber == NULL) {
+        /* Node is at the start of the list. */
+        trackedSuspendedFibers = fiberNode->nextTrackedSuspendedFiber;
+    } else {
+        fiberNode->prevTrackedSuspendedFiber->nextTrackedSuspendedFiber =
+            fiberNode->nextTrackedSuspendedFiber;
+    }
+    if (fiberNode->nextTrackedSuspendedFiber != NULL) {
+        fiberNode->nextTrackedSuspendedFiber->prevTrackedSuspendedFiber =
+            fiberNode->prevTrackedSuspendedFiber;
+    }
+
+    /* If this fiber has a helper thread, we no longer need or want it. */
+    if (fiberNode->fiberHelperThread != NULL) {
+        resumeFiberHelperThread(getEnv(), fiberNode, NULL);
+    }
+
+    fiberNode->isTrackedSuspendedFiber = JNI_FALSE;
+}
+
+static jthread
+getFiberHelperThread(jthread fiber)
+{
+    JNIEnv *env;
+    ThreadNode *fiberNode;
+    jthread helperThread;
+
+    fiberNode = findThread(&runningFibers, fiber);
+    if (fiberNode->fiberHelperThread != NULL) {
+        return fiberNode->fiberHelperThread;
+    }
+
+    env = getEnv();
+
+    /*
+     * We need to mount the fiber on a helper thread. This is done by calling
+     * Fiber.tryMountAndSuspend(), which will create a helper thread for us,
+     * mount the fiber on the thread, suspend the thread, and then return the thread.
+     *
+     * This helper thread is disposed of by resumeFiberHelperThread() when it is 
+     * determined that the helper thread is no longer need (the fiber was resumed,
+     * and we are no longer tracking it).
+     *
+     * Disable all event handling while doing this, since we don't want to deal
+     * with any incoming THREAD_START event.
+     *
+     * Also release the threadLock, or a deadlock will occur when the 
+     * CONTINUATION_RUN event arrives on the helper thread.
+     * fiber fixme: this might not be safe to do.
+     */
+    debugMonitorExit(threadLock);    
+    gdata->ignoreEvents = JNI_TRUE;
+    helperThread = JNI_FUNC_PTR(env,CallObjectMethod)
+        (env, fiber, gdata->fiberTryMountAndSuspend);
+    gdata->ignoreEvents = JNI_FALSE;
+    debugMonitorEnter(threadLock);
+
+
+    if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
+        JNI_FUNC_PTR(env,ExceptionClear)(env);
+        helperThread = NULL;
+    }
+
+    if (helperThread != NULL) {
+        saveGlobalRef(env, helperThread, &(fiberNode->fiberHelperThread));
+        /* Start tracking this fiber as a suspended one. */
+        startTrackingSuspendedFiber(fiberNode);
+    }
+
+    return fiberNode->fiberHelperThread;
+}
+
 static jvmtiError
 commonSuspendByNode(ThreadNode *node)
 {
     jvmtiError error;
 

@@ -947,10 +1098,24 @@
         }
     }
 
     if (error == JVMTI_ERROR_NONE) {
         node->suspendCount++;
+        if (gdata->fibersSupported) {
+            /*
+             * If this is a carrier thread with a mounted fiber, and the fiber
+             * is being tracked, bump the fiber's suspendCount also.
+             */
+            jthread fiber = getThreadFiber(node->thread);
+            if (fiber != NULL) {
+                ThreadNode *fiberNode = findThread(&runningFibers, fiber);
+                if (fiberNode != NULL && fiberNode->isTrackedSuspendedFiber) {
+                    /* If tracking, bump the fiber suspendCount also. */
+                    fiberNode->suspendCount++;
+                }
+            }
+        }
     }
 
     debugMonitorNotifyAll(threadLock);
 
     return error;

@@ -964,10 +1129,26 @@
     if (node->isDebugThread) {
         /* never suspended by debugger => don't ever try to resume */
         return JVMTI_ERROR_NONE;
     }
     if (node->suspendCount > 0) {
+        if (gdata->fibersSupported) {
+            /*
+             * If this is a carrier thread with a mounted fiber, and the fiber
+             * is being tracked, decrement the fiber's suspendCount also.
+             */
+            jthread fiber = getThreadFiber(node->thread);
+            if (fiber != NULL) {
+                ThreadNode *fiberNode = findThread(&runningFibers, fiber);
+                if (fiberNode != NULL && fiberNode->isTrackedSuspendedFiber) {
+                    /* If tracking, decrement the fiber suspendCount also. */
+                    if (fiberNode->suspendCount > 0) {
+                        fiberNode->suspendCount--;
+                    }
+                }
+            }
+        }
         node->suspendCount--;
         debugMonitorNotifyAll(threadLock);
         if ((node->suspendCount == 0) && node->toBeResumed &&
             !node->suspendOnStart) {
             LOG_MISC(("thread=%p resumed", node->thread));

@@ -1047,16 +1228,54 @@
 static jvmtiError
 commonSuspend(JNIEnv *env, jthread thread, jboolean deferred)
 {
     ThreadNode *node;
 
+    if (isFiber(thread)) {
+        jvmtiError error = JVMTI_ERROR_NONE;
+        while (JNI_TRUE) {
+            jthread carrier_thread = getFiberThread(thread);
+            if (carrier_thread != NULL) {
+                /* Fiber is mounted. Suspend the carrier thread. */
+                node = findThread(&runningThreads, carrier_thread);
+                error = suspendThreadByNode(node);
+                if (error != JVMTI_ERROR_NONE) {
+                    LOG_MISC(("commonSuspend: failed to suspend carrier thread(%p)", carrier_thread));
+                    return error;
+                }
+                if (isSameObject(env, carrier_thread, getFiberThread(thread))) {
+                    /* Successfully suspended and still mounted on same carrier thread. */
+                    break;
+                }
+                /* Fiber moved to new carrier thread before it was suspended. Undo and retry. */
+                resumeThreadByNode(node);
+                LOG_MISC(("commonSuspend: fiber mounted on different carrier thread(%p)", carrier_thread));
+            } else {
+                /* Fiber is not mounted. Get a suspended helper thread for it. */
+                ThreadNode *fiberNode = findThread(&runningFibers, thread);
+                if (getFiberHelperThread(thread) == NULL) {
+                    /* fiber fixme: Sometimes the fiber is in a bad state and we can't create a
+                     * helper thread for it. For now we just fail. */
+                    LOG_MISC(("commonSuspend: failed to get fiber helper thread."));
+                    return JVMTI_ERROR_INTERNAL;
+                }
+                fiberNode->suspendCount++;
+                break;
+            }
+        }
+        return error;
+    }
+
     /*
      * 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 ) {

@@ -1360,31 +1579,84 @@
     debugMonitorNotifyAll(threadLock);
 
     return error;
 }
 
-
 static jvmtiError
 commonResume(jthread thread)
 {
     jvmtiError  error;
     ThreadNode *node;
 
+    if (isFiber(thread)) {
+        jthread carrier_thread = getFiberThread(thread);
+        ThreadNode *fiberNode = findThread(&runningFibers, thread);
+        if (carrier_thread == NULL) {
+            /*
+             * Fiber is not mounted on a carrier thread. We may already be tracking this fiber as a
+             * suspended fiber at this point. We would not be if a suspendAll was done, and there was
+             * no suspend of just this fiber. If we are not tracking it, then we need to.
+             */
+            if (fiberNode->isTrackedSuspendedFiber) {
+                if (fiberNode->suspendCount > 0) {
+                    fiberNode->suspendCount--;
+                    /*
+                     * Note, if suspendCount == 0 but suspendAllCount does not, eventually
+                     * threadControl_resumeAll() will be responsible for calling
+                     * stopTrackingSuspendedFiber()
+                     */
+                    if (fiberNode->suspendCount == 0 && suspendAllCount == 0) {
+                        stopTrackingSuspendedFiber(fiberNode);
+                    }
+                }
+            } else {
+                if (suspendAllCount > 0) {
+                    startTrackingSuspendedFiber(fiberNode);
+                    fiberNode->suspendCount--;
+                }
+            }
+            return JVMTI_ERROR_NONE;
+        } else {
+            /*
+             * This is a mounted fiber. If the fiber is being tracked, and the suspendCount
+             * of the carrier thread is 0, then decrement the fiber's suspendCount here
+             * since it cannot be done by resumeThreadByNode because we'll have no way to
+             * get the fiber if the carrier thread is not suspended (getThreadFiber() will
+             * produce a fatal error).
+             */
+            if (fiberNode->isTrackedSuspendedFiber) {
+                if (fiberNode->suspendCount > 0) {
+                    ThreadNode *threadNode = findThread(NULL, thread);
+                    if (threadNode->suspendCount == 0) {
+                        fiberNode->suspendCount--;
+                    }
+                }
+            }
+            /* Fiber is mounted on a carrier thread. Fall through to code below to resume
+             * the carrier thread. */
+            thread = carrier_thread;
+        }
+    }
+
     /*
      * The thread is normally between its start and end events, but if
      * not, check the auxiliary list used by threadControl_suspendThread.
      */
     node = findThread(NULL, thread);
+#if 0
+    tty_message("commonResume: node(%p) suspendCount(%d) %s", node, node->suspendCount, node->name);
+#endif
 
     /*
      * If the node is in neither list, the debugger never suspended
      * this thread, so do nothing.
      */
     error = JVMTI_ERROR_NONE;
     if (node != NULL) {
         error = resumeThreadByNode(node);
     }
+
     return error;
 }
 
 
 jvmtiError

@@ -1432,21 +1704,44 @@
 jvmtiError
 threadControl_suspendCount(jthread thread, jint *count)
 {
     jvmtiError  error;
     ThreadNode *node;
+    jboolean is_fiber = isFiber(thread);
 
     debugMonitorEnter(threadLock);
 
-    node = findThread(&runningThreads, thread);
-    if (node == NULL) {
-        node = findThread(&otherThreads, thread);
+    if (is_fiber) {
+        node = findThread(&runningFibers, thread);
+    } else {
+        node = findThread(&runningThreads, thread);
+        if (node == NULL) {
+            node = findThread(&otherThreads, thread);
+        }
     }
 
     error = JVMTI_ERROR_NONE;
     if (node != NULL) {
-        *count = node->suspendCount;
+        if (!is_fiber) {
+            *count = node->suspendCount;
+        } else {
+            jthread carrier_thread = getFiberThread(thread);
+            if (carrier_thread == NULL) {
+                if (node->isTrackedSuspendedFiber) {
+                    /* Already tracking this fiber, so fiber node owns its suspendCount. */
+                    *count = node->suspendCount;
+                } else {
+                    /* Not tacking this fiber yet, so use suspendAllCount. */
+                    *count = suspendAllCount;
+                }
+            } else {
+                /* It's a mounted fiber, so the carrier thread tracks the suspend count. */
+                node = findThread(&runningThreads, carrier_thread);
+                JDI_ASSERT(node != NULL);
+                *count = node->suspendCount;
+            }
+        }
     } else {
         /*
          * If the node is in neither list, the debugger never suspended
          * this thread, so the suspend count is 0.
          */

@@ -1493,10 +1788,13 @@
 jvmtiError
 threadControl_suspendAll(void)
 {
     jvmtiError error;
     JNIEnv    *env;
+#if 0
+    tty_message("threadControl_suspendAll: suspendAllCount(%d)", suspendAllCount);
+#endif
 
     env = getEnv();
 
     log_debugee_location("threadControl_suspendAll()", NULL, NULL, 0);
 

@@ -1519,13 +1817,11 @@
             error = commonSuspendList(env, count, threads);
             if (error != JVMTI_ERROR_NONE) {
                 goto err;
             }
         } else {
-
             int i;
-
             for (i = 0; i < count; i++) {
                 error = commonSuspend(env, threads[i], JNI_FALSE);
 
                 if (error != JVMTI_ERROR_NONE) {
                     goto err;

@@ -1543,10 +1839,25 @@
             arg.count = count;
             error = enumerateOverThreadList(env, &otherThreads,
                                             suspendAllHelper, &arg);
         }
 
+        /*
+         * Update the suspend count of any fiber that was explicitly suspended
+         * and had a helper thread created for that purpose. These are known
+         * as "tracked" suspended fibers.
+         */
+        debugMonitorEnter(threadLock);
+        {
+            ThreadNode *trackedSuspendedFiber = trackedSuspendedFibers;
+            while (trackedSuspendedFiber != NULL) {
+                trackedSuspendedFiber->suspendCount++;
+                trackedSuspendedFiber = trackedSuspendedFiber->nextTrackedSuspendedFiber;
+            }
+        }
+        debugMonitorExit(threadLock);
+
         if (error == JVMTI_ERROR_NONE) {
             suspendAllCount++;
         }
 
     err: ;

@@ -1572,10 +1883,13 @@
 jvmtiError
 threadControl_resumeAll(void)
 {
     jvmtiError error;
     JNIEnv    *env;
+#if 0
+    tty_message("threadControl_resumeAll: suspendAllCount(%d)", suspendAllCount);
+#endif
 
     env = getEnv();
 
     log_debugee_location("threadControl_resumeAll()", NULL, NULL, 0);
 

@@ -1602,10 +1916,30 @@
 
     if (suspendAllCount > 0) {
         suspendAllCount--;
     }
 
+    /*
+     * Update the suspend count of any fiber that is being tracked. If it is being
+     * tracked, that means that either it was explicitly suspended and had a helper
+     * thread created for helping to suspend it, or it had helper thread created for
+     * the purpose of getting its stack. If the count reaches zero, then stop tracking the fiber.
+     */
+    {
+        ThreadNode *trackedSuspendedFiber = trackedSuspendedFibers;
+        while (trackedSuspendedFiber != NULL) {
+            ThreadNode *fiberNode = trackedSuspendedFiber;
+            trackedSuspendedFiber = trackedSuspendedFiber->nextTrackedSuspendedFiber;
+            if (fiberNode->suspendCount > 0) {
+                fiberNode->suspendCount--;
+            }
+            if (fiberNode->suspendCount == 0 && suspendAllCount == 0) {
+                stopTrackingSuspendedFiber(fiberNode);
+            }
+        }
+    }
+
     debugMonitorExit(threadLock);
     eventHandler_unlock();
     /* let eventHelper.c: commandLoop() know we are resuming */
     unblockCommandLoop();
 

@@ -2029,17 +2363,19 @@
     /* Pretend we were never called */
     return JNI_FALSE;
 }
 
 struct bag *
-threadControl_onEventHandlerEntry(jbyte sessionID, EventIndex ei, jthread thread, jobject currentException)
+threadControl_onEventHandlerEntry(jbyte sessionID, EventInfo *evinfo, jobject currentException)
 {
     ThreadNode *node;
     JNIEnv     *env;
     struct bag *eventBag;
     jthread     threadToSuspend;
     jboolean    consumed;
+    EventIndex  ei = evinfo->ei;
+    jthread     thread = evinfo->thread;
 
     env             = getEnv();
     threadToSuspend = NULL;
 
     log_debugee_location("threadControl_onEventHandlerEntry()", thread, NULL, 0);

@@ -2174,40 +2510,87 @@
     if (ei == EI_THREAD_END) {
         eventHandler_unlock();
     }
 }
 
+void
+threadControl_setName(jthread thread, const char *name)
+{
+#ifdef DEBUG_THREADNAME
+    ThreadNode *node = findThread(NULL, thread);
+    if (node != NULL) {
+        strncpy(node->name, name, sizeof(node->name) - 1);
+    }
+#endif
+}
+
 /* Returns JDWP flavored status and status flags. */
 jvmtiError
 threadControl_applicationThreadStatus(jthread thread,
                         jdwpThreadStatus *pstatus, jint *statusFlags)
 {
-    ThreadNode *node;
+    ThreadNode *node = NULL;
     jvmtiError  error;
     jint        state;
+    jboolean    is_fiber = isFiber(thread);
 
     log_debugee_location("threadControl_applicationThreadStatus()", thread, NULL, 0);
 
     debugMonitorEnter(threadLock);
 
-    error = threadState(thread, &state);
-    *pstatus = map2jdwpThreadStatus(state);
-    *statusFlags = map2jdwpSuspendStatus(state);
-
-    if (error == JVMTI_ERROR_NONE) {
+    if (!is_fiber) {
+        error = threadState(thread, &state);
+        *pstatus = map2jdwpThreadStatus(state);
+        *statusFlags = map2jdwpSuspendStatus(state);
         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.
-             *
-             * Leave suspend status untouched since it is not possible
-             * to distinguish debugger suspends from app suspends.
-             */
-            *pstatus = JDWP_THREAD_STATUS(RUNNING);
+
+        if (error == JVMTI_ERROR_NONE) {
+            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.
+                 *
+                 * Leave suspend status untouched since it is not possible
+                 * to distinguish debugger suspends from app suspends.
+                 */
+                *pstatus = JDWP_THREAD_STATUS(RUNNING);
+            }
+        }
+#if 0
+        tty_message("status thread: node(%p) suspendCount(%d) %d %d %s",
+                    node, node->suspendCount, *pstatus, *statusFlags, node->name);
+#endif
+    } else { /* It's a fiber */
+        int suspendCount;
+        error = JVMTI_ERROR_NONE;
+        *pstatus = JDWP_THREAD_STATUS(RUNNING);
+        *statusFlags = 0;
+        node = findThread(&runningFibers, thread);
+        if (node->isTrackedSuspendedFiber) {
+            /* Already tracking this fiber, so fiber node owns its suspendCount. */
+            suspendCount = node->suspendCount;
+        } else {
+            /* Not tacking this fiber yet, so use suspendAllCount. */
+            suspendCount = suspendAllCount;
+        }
+        if (suspendCount > 0) {
+            *statusFlags = JDWP_SUSPEND_STATUS(SUSPENDED);
+        } else {
+            /* If the fiber was not suspended, maybe it's carrier thread was. */
+            thread = getFiberThread(thread);
+            if (thread != NULL) {
+                node = findThread(&runningThreads, thread);
+                if (node->suspendCount > 0) {
+                    *statusFlags = JDWP_SUSPEND_STATUS(SUSPENDED);
+                }
+            }
         }
+#if 0
+        tty_message("status thread: fiber(%p) suspendCount(%d) %d %d %s",
+                    node, node->suspendCount, *pstatus, *statusFlags, node->name);
+#endif
     }
 
     debugMonitorExit(threadLock);
 
     return error;

@@ -2395,10 +2778,11 @@
     env = getEnv();
     eventHandler_lock(); /* for proper lock order */
     debugMonitorEnter(threadLock);
     (void)enumerateOverThreadList(env, &runningThreads, resetHelper, NULL);
     (void)enumerateOverThreadList(env, &otherThreads, resetHelper, NULL);
+    (void)enumerateOverThreadList(env, &runningFibers, resetHelper, NULL);
 
     removeResumed(env, &otherThreads);
 
     freeDeferredEventModes(env);
 

@@ -2441,10 +2825,59 @@
         /* Thread event */
         ThreadNode *node;
 
         debugMonitorEnter(threadLock);
         {
+            if (isFiber(thread)) {
+                /* fiber fixme: Getting the carrier thread here is just a hack. It does not work if
+                 * the fiber is not mounted, and even if mounted, does not result in the correct
+                 * behaviour if the fiber changes carrier threads. If the carrier thread is
+                 * NULL we need to defer all the code below, most notably
+                 * threadSetEventNotificationMode(), until after the fiber is mounted. We also need
+                 * to call threadSetEventNotificationMode() each time there is an unmount or mount
+                 * since the thread that needs notifications will change as the fiber moves
+                 * between carrier threads. The best way to manage this might be to move
+                 * HandlerNodes for unmounted fibers onto a linked list hanging off the fiber's
+                 * ThreadNode. But that also complicates finding HandlerNodes. For example,
+                 * when a breakpoint is cleared, we call eventHandler_freeByID(), which would
+                 * need to also search every fiber for the handler. The other choice is to
+                 * keep handlers where they are now (off the array of handler chains), but
+                 * for every mount/unmount, search all the handlers in all the chains for
+                 * ones that are for the mounting/unmounting fiber. This could be slow,
+                 * although generally speaking we don't have many HandlerNodes because
+                 * they are generated indirectly by the debugger as users do things
+                 * like set breakpoints.
+                 * A hybrid approach might be best. Keep the handler chains as they are now,
+                 * but also have each fiber maintain a list of its handler nodes for faster
+                 * handling during mount/unmount.
+                 *
+                 * And it should also be noted here that if the carrier thread is null, the
+                 * findThread() call ends up returning the current thread, and then 
+                 * threadSetEventNotificationMode() is called with a NULL thread, resulting
+                 * in the event being enabled on all threads. This bug actually has the 
+                 * desireable affect of making breakpoints that are filtered on an unmounted
+                 * fiber work as expected, because all the carrier threads get the breakpoint
+                 * event enabled. However, for some odd reason it also works as expected if
+                 * the fiber is already mounted. I expected that the breakpoint event would only
+                 * be enabled on the carrier thread in that case, and therefore if the fiber
+                 * was moved to a different carrier thread, you would stop getting breakpoints
+                 * until it moved back to the original carrier thread. That's not the case for some
+                 * reason, and I'm see the breakpoints no matter what carrier thread the fiber
+                 * runs on. It turns out that the agent installs a global breakpoint for
+                 * Thread.resume(), so global breakpoints are always enabled.
+                 * See handleAppResumeBreakpoint.
+                 *
+                 * It also should be noted that this does not cause a problem for single stepping
+                 * because:
+                 *  - There is at most one single step HandlerNode per thread.
+                 *  - Fiber mount/unmount events result explicitly dooing the proper
+                 *    enabling/disabling of the JVMTI single step event on the carrier thread.
+                 * There is a potential issue with initiating a StepRequest on and unmounted
+                 * fiber. See the fixme comment in stepControl_beginStep.
+                 */ 
+                thread = getFiberThread(thread);
+            }
             node = findThread(&runningThreads, thread);
             if ((node == NULL) || (!node->isStarted)) {
                 JNIEnv *env;
 
                 env = getEnv();

@@ -2462,11 +2895,12 @@
 
 /*
  * Returns the current thread, if the thread has generated at least
  * one event, and has not generated a thread end event.
  */
-jthread threadControl_currentThread(void)
+jthread
+threadControl_currentThread(void)
 {
     jthread thread;
 
     debugMonitorEnter(threadLock);
     {

@@ -2497,5 +2931,410 @@
     }
     debugMonitorExit(threadLock);
 
     return frameGeneration;
 }
+
+jthread
+threadControl_getFiberCarrierOrHelperThread(jthread fiber)
+{
+    /* Get the carrier thread that the fiber is running on */
+    jthread carrier_thread = getFiberThread(fiber);
+    if (carrier_thread != NULL) {
+        return carrier_thread;
+    } else {
+        jthread helperThread;
+        debugMonitorEnter(threadLock);
+        helperThread = getFiberHelperThread(fiber);
+        debugMonitorExit(threadLock);
+        if (helperThread == NULL) {
+            /* fiber fixme: we failed to get the helper thread, probably because the fiber
+             * is currently in the PARKING state. Still need a solution for this. Fix
+             * all callers too.
+             */
+            LOG_MISC(("threadControl_getFiberCarrierOrHelperThread: getFiberHelperThread() failed"));
+        }
+        return helperThread;
+    }
+}
+
+jthread *
+threadControl_allFibers(jint *numFibers)
+{
+    JNIEnv *env;
+    ThreadNode *node;
+    jthread* fibers;
+
+    env = getEnv();
+    debugMonitorEnter(threadLock);
+
+    /* Count the number of fibers */
+    /* fiber fixme: we should keep a running total so no counting is needed. */
+    *numFibers = 0;
+    for (node = runningFibers.first; node != NULL; node = node->next) {
+        (*numFibers)++;
+    }
+
+    /* Allocate and fill in the fibers array. */
+    fibers = jvmtiAllocate(*numFibers * sizeof(jthread*));
+    if (fibers != NULL) {
+        int i = 0;
+        for (node = runningFibers.first; node != NULL;  node = node->next) {
+            fibers[i++] = node->thread;
+        }
+    }
+
+    debugMonitorExit(threadLock);
+
+    return fibers;
+}
+
+jboolean threadControl_isKnownFiber(jthread fiber) {
+    ThreadNode *fiberNode;
+    debugMonitorEnter(threadLock);
+    fiberNode = findThread(&runningFibers, fiber);
+    debugMonitorExit(threadLock);
+    return fiberNode != NULL;
+}
+
+void
+threadControl_addFiber(jthread fiber)
+{
+    ThreadNode *fiberNode;
+    debugMonitorEnter(threadLock);
+    fiberNode = insertThread(getEnv(), &runningFibers, fiber);
+    debugMonitorExit(threadLock);
+}
+
+void
+threadControl_mountFiber(jthread fiber, jthread thread, jbyte sessionID) {
+    /* fiber fixme: this funciton no longer serves any purpose now that we rely on
+     * continuation events instead. remove.
+     */
+}
+
+
+void
+threadControl_unmountFiber(jthread fiber, jthread thread)
+{
+    /* fiber fixme: this funciton no longer serves any purpose now that we rely on
+     * continuation events instead. remove.
+     */
+}
+
+void
+threadControl_continuationRun(jthread thread, jint continuation_frame_count)
+{
+    debugMonitorEnter(threadLock);
+    {
+        JNIEnv *env = getEnv();
+        ThreadNode *threadNode;
+        ThreadNode *fiberNode;
+        jthread fiber;
+
+        threadNode = findThread(&runningThreads, thread);
+
+        /*
+         * fiber fixme: For now, NULL implies that this is a helper thread created by
+         * getFiberHelperThread(). We should actually verify that, but for now just
+         * assume it is the case and ignore the event. The need for helper threads will
+         * hopefully go away, in which case the assert can be re-added.
+         */
+        //JDI_ASSERT(threadNode != NULL);
+        if (threadNode == NULL) {
+            debugMonitorExit(threadLock);
+            return;
+        }
+
+        JDI_ASSERT(threadNode->isStarted);
+        JDI_ASSERT(bagSize(threadNode->eventBag) == 0);
+
+        if (threadNode->currentStep.pending) {
+            /*
+             * If we are doing a STEP_INTO and are doing class filtering (usually library
+             * classes), we are relying on METHOD_ENTRY events to tell us if we've stepped
+             * back into user code. We won't get this event if when we resume the
+             * continuation, so we need to let the stepControl now that we got a
+             * CONTINUATION_RUN event so it can do the right thing in absense of
+             * the METHOD_ENTRY event. There's also a FramePop setup situation that
+             * stepControl needs to deal with, which is another reason it needs to
+             * know about CONTINUATION_RUN events.
+             */
+            stepControl_handleContinuationRun(env, thread, &threadNode->currentStep);
+        }
+
+        fiber = getThreadFiber(threadNode->thread);
+        if (fiber == NULL) {
+            debugMonitorExit(threadLock);
+            return; /* Nothing more to do if thread is not executing a fiber. */
+        }
+
+        fiberNode = findThread(&runningFibers, fiber);
+        if (!gdata->notifyDebuggerOfAllFibers && fiberNode == NULL) {
+            /* This is not a fiber we are tracking, so nothing to do. */
+            debugMonitorExit(threadLock);
+            return;
+        }
+
+        JDI_ASSERT(fiberNode != NULL);
+        JDI_ASSERT(fiberNode->isStarted);
+        JDI_ASSERT(bagSize(fiberNode->eventBag) == 0);
+
+        /* If we are not single stepping in this fiber then there is nothing to do. */
+        if (!fiberNode->currentStep.pending) {
+            debugMonitorExit(threadLock);
+            return;
+        }
+        JDI_ASSERT(fiberNode->currentStep.is_fiber);
+
+        /*
+         * Move the single step state from the fiberNode to threadNode, but only if we aren't
+         * already single stepping on the carrier thread.
+         */
+        if (!threadNode->currentStep.pending) {
+            /* Copy fiber currentStep struct to carrier thread. */
+            memcpy(&threadNode->currentStep, &fiberNode->currentStep, sizeof(fiberNode->currentStep));
+
+            /* Enable JVMTI single step on the carrier thread if necessary. */
+            if (fiberNode->instructionStepMode == JVMTI_ENABLE) {
+                stepControl_enableStepping(thread);
+                threadNode->instructionStepMode = JVMTI_ENABLE;
+            }
+
+            /* Restore the NotifyFramePop that was in place when this Fiber yielded. */
+            {
+                jvmtiError error;
+                jint depth;
+                /* NotifyFramePop was originally called with a depth of 0 to indicate the current
+                 * frame. However, frames have been pushed since then, so we need to adjust the
+                 * depth to get to the right frame.
+                 *
+                 * fromStackDepth represents the number of frames on the stack when the STEP_OVER
+                 * was started. NotifyFramePop was called on the method that was entered, which is
+                 * one frame below (fromStackDepth + 1). To account for new frames pushed since
+                 * then, we subtract fromStackDepth from the current number of frames. This
+                 * represents the frame where the STEP_OVER was done, but since we want one
+                 * frame below this point, we also subtract one.
+                 */
+                depth = getThreadFrameCount(thread) - fiberNode->currentStep.fromStackDepth;
+                depth--; /* We actually want the frame one below the adjusted fromStackDepth. */
+                if (depth >= 0) {
+                    error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)(gdata->jvmti, thread, depth);
+                    if (error == JVMTI_ERROR_DUPLICATE) {
+                      error = JVMTI_ERROR_NONE;
+                      /* Already being notified, continue without error */
+                    } else if (error != JVMTI_ERROR_NONE) {
+                      EXIT_ERROR(error, "NotifyFramePop failed during mountFiber");
+                    }
+                } else {
+                    /*
+                     * If the less than 0, then that means we were single stepping over
+                     * the Continuation.doYield() call. In this case NotifyFramePop is not going to work
+                     * since there was never one setup (doYield() was never actually entered). So
+                     * all that needs to be done is to restore single stepping, and we'll stop
+                     * on the next bytecode after the doYield() call.
+                     */
+                    JDI_ASSERT(depth == -1);
+                    if (fiberNode->instructionStepMode == JVMTI_DISABLE) {
+                      stepControl_enableStepping(thread);
+                      threadNode->instructionStepMode = JVMTI_ENABLE;
+                    }
+                }
+            }
+   
+            /* Enable events */
+            threadControl_setEventMode(JVMTI_ENABLE, EI_EXCEPTION_CATCH, thread);
+            threadControl_setEventMode(JVMTI_ENABLE, EI_FRAME_POP, thread);
+            if (threadNode->currentStep.methodEnterHandlerNode != NULL) {
+                threadControl_setEventMode(JVMTI_ENABLE, EI_METHOD_ENTRY, thread);
+            }
+        }
+
+        /* Always clear the fiber single step state, regardless of what we've done above. */
+        fiberNode->instructionStepMode = JVMTI_DISABLE;
+        memset(&fiberNode->currentStep, 0, sizeof(fiberNode->currentStep));
+
+        /*
+         * If for any reason we are tracking this fiber, then that must mean during a 
+         * suspendAll there was a resume done on this fiber. So we started tracking it
+         * and decremented its suspendCount (which normally would put it at 0).
+         */
+        if (fiberNode->isTrackedSuspendedFiber) {
+            JDI_ASSERT(suspendAllCount > 0 && fiberNode->suspendCount == 0);
+        }
+        if (suspendAllCount > 0) {
+            /*
+             * If there is an outstanding suspendAll, then we suspend the carrier thread. The
+             * way this typically ends up happening is if initially all threads were suspended
+             * (perhaps when a breakpoing was hit), and then the debugger user decides to resume
+             * the fiber or carrier thread. This could allow a new fiber to be mounted on the
+             * carrier thread, but the fiber is implied to be suspended because suspendAllCount
+             * is >0. In order to keep the fiber from running we must suspened the carrier thread.
+             */
+            /* fiber fixme XXX: disable this feature for now. */
+            //eventHelper_suspendThread(sessionID, thread);
+        }
+    }
+    debugMonitorExit(threadLock);
+}
+
+void
+threadControl_continuationYield(jthread thread, jint continuation_frame_count)
+{
+    /* fiber fixme: need to figure out what to do with these 4 ThreadNode fields:
+       unsigned int popFrameEvent : 1;
+       unsigned int popFrameProceed : 1;
+       unsigned int popFrameThread : 1;
+       InvokeRequest currentInvoke;
+    */
+    debugMonitorEnter(threadLock);
+    {
+        JNIEnv *env = getEnv();
+        ThreadNode *threadNode;
+        jint total_frame_count;
+        jint fromDepth;
+
+        threadNode = findThread(&runningThreads, thread);
+
+        /*
+         * fiber fixme: For now, NULL implies that this is a helper thread created by
+         * getFiberHelperThread(). We should actually verify that, but for now just
+         * assume it is the case and ignore the event. The need for helper threads will
+         * hopefully go away, in which case the assert can be re-added.
+         */
+        //JDI_ASSERT(threadNode != NULL);
+        if (threadNode == NULL) {
+            debugMonitorExit(threadLock);
+            return; /* Nothing to do if thread is not known */
+        }
+
+        JDI_ASSERT(threadNode->isStarted);
+        JDI_ASSERT(bagSize(threadNode->eventBag) == 0);
+
+        /*
+         * If we are not single stepping in this thread, then there is nothing to do.
+         */
+        if (!threadNode->currentStep.pending) {
+            debugMonitorExit(threadLock);
+            return; /* Nothing to do. */
+        }
+
+        /* At what depth were we single stepping. */
+        fromDepth = threadNode->currentStep.fromStackDepth;
+
+        /*
+         * Note the continuation has already been unmounted, so total_frame_count will not
+         * include the continuation frames.
+         */
+        total_frame_count = getThreadFrameCount(thread);
+
+        if (threadNode->currentStep.depth == JDWP_STEP_DEPTH(OVER) &&
+            total_frame_count == fromDepth) {
+            /*
+             * We were stepping over Continuation.doContinue() in Continuation.run(). This
+             * is a special case. Before the continuation was unmounted do to the yield, the
+             * stack looked like:
+             *    java.lang.Continuation.yield0
+             *    java.lang.Continuation.yield
+             *    <fiber frames>  <-- if Fiber, otherwise just additional continuation frames
+             *    java.lang.Continuation.enter  <-- bottommost continuation frame
+             *    java.lang.Continuation.run    <-- doContinue() call jumps into continuation
+             *    java.lang.Fiber.runContinuation  <-- if Fiber, otherwise will be different
+             *    <scheduler frames>
+             * All frames above run(), starting with enter(), are continuation frames. The
+             * correct thing to do here is just enable single stepping. This will resume single
+             * stepping in Continuation.run() right after the Continuation.doContinue() call.
+             */
+            JDI_ASSERT(threadNode->instructionStepMode == JVMTI_DISABLE);
+            {
+                stepControl_enableStepping(thread);
+                threadNode->instructionStepMode = JVMTI_ENABLE;
+            }
+        } else if (!threadNode->currentStep.is_fiber) {
+            /* We were single stepping, but not in a fiber. */
+            if (total_frame_count < fromDepth) { /* Check if fromDepth is in the continuation. */
+                /*
+                 * This means the frame we were single stepping in was part of the set of
+                 * frames that will were frozen when this continuation yielded. Because of that
+                 * we need to re-enable single stepping because we won't ever be getting
+                 * the FRAME_POP event for returning to that frame. This will resume single
+                 * stepping in Continuation.run() right after the Continuation.enter() call.
+                 */
+                if (threadNode->instructionStepMode == JVMTI_DISABLE) {
+                    stepControl_enableStepping(thread);
+                    threadNode->instructionStepMode = JVMTI_ENABLE;
+                }
+            } else {
+                /*
+                 * We are not single stepping in the continuation, and from the earlier check we
+                 * know we are not single stepping in Continuation.run(), because that would imply
+                 * we were single stepping over the doContinue() call, and we already checked
+                 * for that. There is nothing to do in this case. A NotifyFramePop is already setup
+                 * for a frame further up the stack.
+                 */
+            }
+        } else {
+            /*
+             * We are single stepping the fiber, not the carrier thread. Move the single step
+             * state to the fiberNode.
+             */
+            jthread fiber = getThreadFiber(thread);
+            ThreadNode *fiberNode;
+            JDI_ASSERT(fiber != NULL);
+
+            fiberNode = findThread(&runningFibers, fiber);
+            if (!gdata->notifyDebuggerOfAllFibers && fiberNode == NULL) {
+                /* This is not a fiber we are tracking. */
+                debugMonitorExit(threadLock);
+                return;
+            }
+
+            JDI_ASSERT(fiberNode != NULL);
+            JDI_ASSERT(fiberNode->isStarted);
+            JDI_ASSERT(bagSize(fiberNode->eventBag) == 0);
+
+            if (threadNode->currentStep.depth == JDWP_STEP_DEPTH(INTO) &&
+                (total_frame_count + continuation_frame_count == fromDepth)) {
+                /* We are stepping into Continuation.doYield(), so leave single stepping enabled.
+                 * This will resume single stepping in Continuation.run() right after the
+                 * Continuation.enter() call.
+                 */
+            } else if (total_frame_count >= fromDepth) { /* Check if fromDepth is NOT in the continuation. */
+                /*
+                 * This means the single stepping was initiated stepping in a fiber, but in that small
+                 * window after Thread.setFiber(this) has been called, and before the fiber's
+                 * continuation was actually mounted. An example of this is stepping over the cont.run()
+                 * call in Fiber.runContinuation(). In this case we just leave the carrier thread's
+                 * single step state in place. We should eventually get a FramePop event to 
+                 * enable single stepping again.
+                 */
+                JDI_ASSERT(threadNode->currentStep.depth == JDWP_STEP_DEPTH(OVER));
+            } else {
+                /*
+                 * We were single stepping in the fiber, and now we need to stop doing that since
+                 * we are leaving the fiber. We will copy our single step state from the carrier
+                 * thread to the fiber so we can later restore it when the fiber is mounted again
+                 * and we get a CONTINUATION_RUN event.
+                 */
+
+                /* Clean up JVMTI SINGLE_STEP state. */
+                if (threadNode->instructionStepMode == JVMTI_ENABLE) {
+                    stepControl_disableStepping(thread);
+                    threadNode->instructionStepMode = JVMTI_DISABLE;
+                    fiberNode->instructionStepMode = JVMTI_ENABLE;
+                }
+   
+                /* Disable events */
+                threadControl_setEventMode(JVMTI_DISABLE, EI_EXCEPTION_CATCH, thread);
+                threadControl_setEventMode(JVMTI_DISABLE, EI_FRAME_POP, thread);
+                if (threadNode->currentStep.methodEnterHandlerNode != NULL) {
+                    threadControl_setEventMode(JVMTI_DISABLE, EI_METHOD_ENTRY, thread);
+                }
+
+                /* Copy currentStep struct from the threadNode to the fiberNode and then zero out the threadNode. */
+                memcpy(&fiberNode->currentStep, &threadNode->currentStep, sizeof(threadNode->currentStep));
+                memset(&threadNode->currentStep, 0, sizeof(threadNode->currentStep));
+            }
+        }
+    }
+    debugMonitorExit(threadLock);
+}
< prev index next >