java - JDK问题: interrupt another thread blocked on an IO operation with signal may fail

标签 java linux multithreading kernel signals

OpenJDK 实现(linux 部分)大量使用信号来中断在 native IO 操作上阻塞的另一个线程。基本思想是,向目标线程发送唤醒信号将导致阻塞 IO 调用返回 EINTR。

一个例子是,异步关闭使用的 fd 时中断阻塞的线程:

https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/solaris/native/java/net/linux_close.c

    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;
        }
        ...
    }

另一个例子是中断一个套接字 channel :

https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/nio/ch/SocketChannelImpl.java

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 <stdio.h>
#include <signal.h>
#include <unistd.h>

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 错误还是我遗漏了什么?

最佳答案

“这是 JDK 错误还是我遗漏了什么?”

您错过了重要的一点:错误是规范与实现之间的差异。 Java 规范对 linux 信号只字未提,这意味着它可以以任何方式对它们使用react,这不能被视为错误。

要证明 JDK 中存在错误,您必须在 JVM spec 中找到一个断言你认为违反了,并创建一个测试来证明 JVM 的行为与该断言规定的不同。由于 JVM 规范中的所有断言都是用字节码表示的,因此测试必须用 Java(或任何其他 JVM 语言)编写并编译为类文件。

关于java - JDK问题: interrupt another thread blocked on an IO operation with signal may fail,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53691013/

相关文章:

java - 0xp0 打印 0.0(十六进制浮点文字)

linux - 如何在网页中自动填写表格

linux - 301 由 google 索引的 url 包含/index.php/*

linux - Bash 帮助删除嵌套目录

c# - 在递归调用中使用 lock(obj)

c# - 在对象的实例上为同一方法创建多个线程

java - 为什么多态性在 Java 中有用?

java - 如何编辑转换为图像的文本?或任何其他实现/编辑文本的方法

java - 游戏循环完全卡住了我的程序

java - 如何提高大文件的加密/解密性能