NDK 可以处理信号吗?

Can NDK handle signals?

这是一个对照实验,显示 NDK 无法处理主机设法处理的 SIGUSR1:https://github.com/champignoom/TestNDKBug。但是从我发现的任何文档中都不能明显看出 NDK 无法处理信号。

有人知道这里发生了什么吗?

repo 基本上是一个简单的 C 实验的包装器,可以为设备和主机编译,其中唯一的工作线程应该在其 10 次迭代的第 5 次被 SIGUSR1 中断:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>

#ifdef TEST_HOST
#define printlog(...) fprintf(stderr, __VA_ARGS__)
#else
#include <jni.h>
#include <android/log.h>
#define printlog(...) __android_log_print(ANDROID_LOG_DEBUG, "TestNDKBug", __VA_ARGS__)
#endif

static const struct timespec DURATION = { .tv_sec = 0, .tv_nsec = 200LL*1000*1000 };
static const struct timespec DURATION5 = { .tv_sec = 1, .tv_nsec = 0 };

static void *do_thread(void *_arg) {
    for (int i=0; i<10; ++i) {
        printlog("loop %d\n", i);
        if (nanosleep(&DURATION, NULL)) {
            perror("nanosleep thread");
        }
    }
    return NULL;
}

static void signal_handler(int _sig) {
    fprintf(stderr, "signal received\n");
    pthread_exit(NULL);
}

void do_main() {
    struct sigaction sa;
    sa.sa_flags = 0;
    sa.sa_restorer = NULL;
    sa.sa_sigaction = NULL;
    sa.sa_handler = signal_handler;
    if (sigemptyset(&sa.sa_mask)) {
        perror("sigemptyset");
        exit(1);
    }
    if (sigaction(SIGUSR1, &sa, NULL)) {
        perror("sigaction");
        exit(1);
    }

    pthread_t pid;
    if (pthread_create(&pid, NULL, do_thread, NULL)) {
        perror("pthread_create");
        exit(1);
    }

    if (nanosleep(&DURATION5, NULL)) {
        perror("nanosleep main");
    }

    if (pthread_kill(pid, SIGUSR1)) {
        perror("pthread_kill");
        exit(1);
    }

    if (pthread_join(pid, NULL)) {
        perror("pthread_join");
    }
}

#ifdef TEST_HOST
int main() {
    do_main();
    return 0;
}
#else
JNIEXPORT void JNICALL
Java_com_champignoom_testndkbug_NDKWrapper_run_1native(JNIEnv *env, jclass clazz) {
    do_main();
}
#endif

循环未在设备上中断:

...
2021-02-08 15:36:48.273 21420-21454/com.champignoom.testndkbug I/TestRunner: started: test(com.champignoom.testndkbug.InstrumentedTest)
2021-02-08 15:36:48.279 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 0
2021-02-08 15:36:48.479 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 1
2021-02-08 15:36:48.679 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 2
2021-02-08 15:36:48.880 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 3
2021-02-08 15:36:49.082 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 4
2021-02-08 15:36:49.283 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 5
2021-02-08 15:36:49.483 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 6
2021-02-08 15:36:49.684 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 7
2021-02-08 15:36:49.885 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 8
2021-02-08 15:36:50.085 21420-21456/com.champignoom.testndkbug D/TestNDKBug: loop 9
2021-02-08 15:36:50.286 21420-21454/com.champignoom.testndkbug I/TestRunner: finished: test(com.champignoom.testndkbug.InstrumentedTest)
...

但在主机上被中断了(gcc -DTEST_HOST app/src/main/cpp/native-lib.c -lpthread -std=gnu11 -o ./test-ndk-bug && ./test-ndk-bug):

loop 0
loop 1
loop 2
loop 3
loop 4
signal received

我们将不胜感激。

注意:AttachCurrentThread/DetachCurrentThread 经测试与本实验无关。

事实证明,SIGUSR1 默认情况下被阻止,尽管没有被处理。 只需在 pthread_create 之前解锁 SIGUSR1 即可。