本文介紹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是最先需要喚醒的.
該方法的調用點爲:
-
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會在後續文章中介紹.
-
異步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 }
-
在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;
}
}
}
此方法是在線程終止,線程中斷時調用.關於如何調用,同樣是在後續文章中介紹.