OpenJDK实现(linux部分)使用信号来中断在本机IO操作上阻塞的另一个线程 . 基本思想是,向目标线程发送唤醒信号将导致阻塞IO调用返回EINTR .
例如,在异步关闭使用的fd时中断被阻塞的线程:
static int closefd(int fd1, int fd2) {
...
/*
* Send a wakeup signal to all threads blocked on this
* file descriptor.
*/
threadEntry_t *curr = fdEntry->threads;
while (curr != NULL) {
curr->intr = 1;
pthread_kill( curr->thr, sigWakeup );
curr = curr->next;
}
...
}
另一个例子是中断套接字通道:
protected void implCloseSelectableChannel() throws IOException {
...
// Signal native threads, if needed. If a target thread is not
// currently blocked in an I/O operation then no harm is done since
// the signal handler doesn't actually do anything.
//
if (readerThread != 0)
NativeThread.signal(readerThread);
if (writerThread != 0)
NativeThread.signal(writerThread);
...
}
但是,可能存在阻塞线程将错过信号并且不会被中断的竞赛 .
为了演示,我写了一小段测试代码:
#include
#include
#include
void handler(int sig) {
printf("singal\n");
}
int main() {
// set-up an interrupting signal handler
signal(SIGINT, handler);
// not blocking operation, prepare to block
// if the wake-up signal arrived during this
// operation. we'll miss it.
for (int i = 0; i < 2100000000; ++i) {
int j = i * i;
}
printf("not blocking operation done\n");
// blocking io operation
unsigned int remain = sleep(10);
printf("blocking io operation done, remain %u\n", remain);
return 0;
}
如果唤醒信号在被阻塞的线程进入内核之前到达以等待IO,例如执行一些glibc的包装代码,则被阻塞的线程将永远错过该信号并且未被中断 .
信号在IO之前到达时的输出如下:
./a.out
^Csingal
^Csingal
^Csingal
^Csingal
^Csingal
not blocking operation done
blocking io operation done, remain 0
当IO阻塞后信号到达时的输出:
./a.out
not blocking operation done
^Csingal
blocking io operation done, remain 6
这是一个JDK错误还是我错过了什么?