本文介绍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;
}
}
}
此方法是在线程终止,线程中断时调用.关于如何调用,同样是在后续文章中介绍.