kvm線程-003

本文介紹kvm中關於線程操作中的下列函數:

registerAlarm
checkTimerQueue
removePendingAlarm

這三個函數,涉及到了一個隊列–TimerQueue.其定義如下:

THREAD TimerQueue;
typedef struct threadQueue*         THREAD;

而threadQueue(說是queue,其數據結構上是用鏈表實現的隊列)就是kvm內部的線程,這點在kvm線程-001 中有介紹.

registerAlarm

其中的代碼如下:

void
registerAlarm(THREAD thread, long64 delta, void (*wakeupCall)(THREAD))
{
#if NEED_LONG_ALIGNMENT
    Java8 tdub;
#endif

    THREAD q, prev_q;
    ulong64 wakeupTime;

    // 1. 在隊列中查找,如果有的話,則直接return
    q = TimerQueue;
    while (q != NULL) {
        if (q == thread)
            return; /* already on the queue so leave  */
        q = q->nextAlarmThread;
    }
    /* 2. 計算wakeupTime ,並保存在thread 中 */
    wakeupTime = CurrentTime_md();
    ll_inc(wakeupTime, delta);      /* wakeUp += delta */

    SET_ULONG(thread->wakeupTime, wakeupTime);

#if INCLUDEDEBUGCODE
    if (tracethreading) {
        TraceThread(thread, "Adding to timer queue");
    }
#endif

    /* 3. 保存回調 */
    thread->wakeupCall = wakeupCall;

    /* 4. 插入隊列*/
    q = TimerQueue;
    prev_q = NULL;
    while (q != NULL) {
        ulong64 qtime = GET_ULONG(q->wakeupTime);
        if (ll_compare_ge(qtime, wakeupTime)) { // !ll_compare_lt(qtime,wakeupTime) = ! qtime < wakeupTime 也就是qtime >=wakeupTime
            break;
        }
        prev_q = q;
        q = q->nextAlarmThread;
    }
    if (prev_q != NULL) {  // 插入
        /* This is the first item in the queue. */
        prev_q->nextAlarmThread = thread;
        thread->nextAlarmThread = q;
    } else {// 如果是等於null,則意味着目前沒有TimerQueue
        thread->nextAlarmThread = TimerQueue;
        TimerQueue = thread;
    }
}

注意一點: TimerQueue中的元素是按照喚醒時間(wakeupTime)的先後順序排列的.其head是最先需要喚醒的.

該方法的調用點爲:

  1. java.lang.thread.sleep方法.在Thread中,sleep方法爲本地方法,定義如下:

     public static native void sleep(long millis) throws InterruptedException;
    

    而在kvm中,最終對應的方法爲:

    void Java_java_lang_Thread_sleep(void)
     {
         long64  period;
         THREAD thisThread = CurrentThread; // 獲得執行線程
     
         popLong(period); // 獲得要等待的時間
         if (ll_zero_lt(period)) { // 如果period < 0 ,則拋出IllegalArgumentException
             raiseException(IllegalArgumentException);
         } else  if (thisThread->isPendingInterrupt) { // 如果線程正在Interrupt,則調用handlePendingInterrupt
             handlePendingInterrupt();
         } else if (ll_zero_gt(period)) { // 如果>=0。則將線程掛起,同時加入timer隊列,等時間到時,則調用resumeThread方法進行恢復
             /* Suspend the current thread before we add the timer */
             suspendThread();
     
             /* Now add the timer (this is the safe way) */
             registerAlarm(thisThread, period, resumeThread);
         } else if (ll_zero_eq(period)) {
             signalTimeToReschedule();
         }
        }
    

    在該方法中的其他方法handlePendingInterrupt, signalTimeToReschedule會在後續文章中介紹.

  2. 異步i/o.代碼如下:

    #ifndef GENERIC_IO_WAIT_TIME
     #define GENERIC_IO_WAIT_TIME 0
     #endif
    
     void Java_com_sun_cldc_io_Waiter_waitForIO(void)
     {
     #if GENERIC_IO_WAIT_TIME > 0
         /* Suspend the current thread for GENERIC_IO_WAIT_TIME milliseconds */
         THREAD thisThread = CurrentThread;
         suspendThread();
         // 加入到timer隊列中,進行恢復
         registerAlarm(thisThread, (long64)GENERIC_IO_WAIT_TIME, resumeThread);
     #else
         /* Try to switch to another thread 切換爲其他線程*/
         signalTimeToReschedule();
     #endif
     }
    
  3. 在monitorWait中調用.代碼如下:

    // 如果是有限等待的話,則加入到等待隊列中以喚醒
    if (ll_zero_gt(delta)) {
        registerAlarm(CurrentThread, delta, monitorWaitAlarm);
    }
    

    關於monitorWait,會在後續文章中介紹.

checkTimerQueue

此處的代碼如下:

oid
checkTimerQueue(ulong64 *nextTimerDelta)
{
#if NEED_LONG_ALIGNMENT
    Java8 tdub;
#endif
    ulong64 now = CurrentTime_md();
    // 1. 如果代碼存在的話
    if (TimerQueue != NULL) {
        do {
            ulong64 firstTime = GET_ULONG(TimerQueue->wakeupTime); // 獲得隊首的喚醒時間
            if (ll_compare_le(firstTime, now)) {// 如果小於當前時間的話,則需要喚醒
                
                THREAD thread = TimerQueue;
                void (*wakeupCall)() = thread->wakeupCall;

#if INCLUDEDEBUGCODE
                if (tracethreading) {
                    TraceThread(thread, "Removing from timer queue");
                }
#endif

                TimerQueue = thread->nextAlarmThread;
                thread->nextAlarmThread = NULL;
                thread->wakeupCall = NULL; /* signal that not on queue */
                wakeupCall(thread);
            } else {
                break;
            }
        } while (TimerQueue != NULL);
    }

    /* 2. 計算下次喚醒的時間 */
    if (TimerQueue == NULL) { // 如果TimerQueue = null,則將nextTimerDelta = 0 
        ll_setZero(*nextTimerDelta);
    } else {
        ulong64 nextwakeup = GET_ULONG(TimerQueue->wakeupTime);
        if (ll_compare_le(nextwakeup, now)) { // 如果nextwakeup <= now,則設置nextTimerDelta 爲0
            ll_setZero(*nextTimerDelta);
        } else {
                ll_dec(nextwakeup, now);
            *nextTimerDelta = nextwakeup; // 否則,就設置nextTimerDelta爲nextwakeup 和now的差值
        }
    }
}

此方法只在reschedule()中調用,如下:

#define reschedule()                                                \
     do  {                                                          \
        ulong64 wakeupDelta;                                        \
        if (!areAliveThreads()) {                                   \
            return;   /* end of program */                          \
        }                                                           \
        checkTimerQueue(&wakeupDelta);                              \
        InterpreterHandleEvent(wakeupDelta);                        \
        __ProcessDebugCmds(0);                                      \
    } while (!SwitchThread());

關於這點,在後續文章中介紹

removePendingAlarm

此處的代碼爲:

static void
removePendingAlarm(THREAD thread) {
    THREAD q, prev_q;

#if INCLUDEDEBUGCODE
    if (tracethreading) {
        TraceThread(thread, "Purging from timer queue");
    }
#endif

    for (q = TimerQueue, prev_q = NULL;
               q != NULL; prev_q = q, q = q->nextAlarmThread) {
        if (q == thread) {// 從隊列中刪除
            if (prev_q) {
                prev_q->nextAlarmThread = q->nextAlarmThread;
            } else {
                TimerQueue = q->nextAlarmThread;
            }
            q->nextAlarmThread = NULL;
            q->wakeupCall = NULL; 
            break;
        }
    }
}

此方法是在線程終止,線程中斷時調用.關於如何調用,同樣是在後續文章中介紹.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章