Реализация OpenJDK (часть linux) много использует сигнал для прерывания другого потока, заблокированного в собственной операции ввода-вывода.Основная идея заключается в том, что отправка сигнала пробуждения целевому потоку приведет к возврату блокирующего вызова 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;
}
...
}
другой пример - прерывание канала сокета:
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;
}
Если сигнал пробуждения поступил непосредственно перед тем, как заблокированный поток входит в ядро, чтобы ожидать ввода-вывода, например, выполняяКакой-то код-обертка glibc, заблокированный поток пропустит этот сигнал навсегда и не сможет быть прерван.
Вывод, когда сигнал поступил до ввода / вывода, выглядит следующим образом:
./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 или я что-то пропустил?