英文:
C pthreads - Why is `sleep` interrupted by a thread cancellation request despite cancel state being disabled?
问题
I'm currently learning C and POSIX APIs, in particular pthreads. I've encountered the following situation which surprised me and seemed like it isn't supposed to be that way.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
static void *thread(void *arg)
{
int oldstate;
printf("other thread: started\n");
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
printf("other thread: started doing thing that can't be canceled\n");
errno = 0;
sleep(10);
if (errno)
perror("sleep");
printf("other thread: finished doing thing that can't be canceled\n");
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
printf("other thread: exiting\n");
return NULL;
}
int main(void)
{
pthread_t thr;
void *res;
pthread_create(&thr, NULL, thread, NULL);
printf("main: created other thread\n");
printf("main: letting other thread work for 3 seconds...\n");
sleep(3);
printf("main: canceling other thread\n");
pthread_cancel(thr);
printf("main: joining with other thread\n");
res = NULL;
pthread_join(thr, &res);
if (res == PTHREAD_CANCELED)
printf("main: canceled other thread\n");
else
printf("main: other thread result: %p\n", res);
printf("main: exiting\n");
return 0;
}
I expected the following output from this program:
main: created other thread
main: letting other thread work for 3 seconds...
other thread: started
other thread: started doing thing that can't be canceled
main: canceling other thread
main: joining with other thread
other thread: finished doing thing that can't be canceled
other thread: exiting
main: canceled other thread
main: exiting
However, sleep
gets interrupted, and the output is as follows:
main: created other thread
main: letting other thread work for 3 seconds...
other thread: started
other thread: started doing thing that can't be canceled
main: canceling other thread
main: joining with other thread
sleep: Interrupted system call
other thread: finished doing thing that can't be canceled
other thread: exiting
main: canceled other thread
main: exiting
This just seems wrong. Cancellation request is not a signal, right? Then why does it cause sleep
to be interrupted? I expected the code to execute as if instead of just sleep(10)
, there was the following code:
// before `thread`:
static pthread_mutex_t sleep_mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sleep_cv = PTHREAD_COND_INITIALIZER;
static void *sleep_thread(void *arg)
{
int sleep_for = *((int *) arg);
sleep(sleep_for);
pthread_cond_broadcast(&sleep_cv);
return NULL;
}
static void *thread(void *arg)
{
// ...
// instead of `sleep`:
pthread_mutex_lock(&sleep_mut);
pthread_t sleep_thr;
int sleep_for = 10;
pthread_create(&sleep_thr, NULL, sleep_thread, &sleep_for);
pthread_cond_wait(&sleep_cv, &sleep_mut);
pthread_mutex_unlock(&sleep_mut);
// ...
}
英文:
I'm currently learning C and POSIX APIs, in particular pthreads. I've encountered following situation which surprised me and seemed like it isn't supposed to be that way.
The sleep
call inside a thread is being interrupted by a cancellation request from the main thread, despite cancel state set to disabled in that thread.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
static void *thread(void *arg)
{
int oldstate;
printf("other thread: started\n");
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
printf("other thread: started doing thing that can't be canceled\n");
errno = 0;
sleep(10);
if (errno)
perror("sleep");
printf("other thread: finished doing thing that can't be canceled\n");
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
printf("other thread: exiting\n");
return NULL;
}
int main(void)
{
pthread_t thr;
void *res;
pthread_create(&thr, NULL, thread, NULL);
printf("main: created other thread\n");
printf("main: letting other thread work for 3 seconds...\n");
sleep(3);
printf("main: canceling other thread\n");
pthread_cancel(thr);
printf("main: joining with other thread\n");
res = NULL;
pthread_join(thr, &res);
if (res == PTHREAD_CANCELED)
printf("main: canceled other thread\n");
else
printf("main: other thread result: %p\n", res);
printf("main: exiting\n");
return 0;
}
I expected the following output from this program:
main: created other thread
main: letting other thread work for 3 seconds...
other thread: started
other thread: started doing thing that can't be canceled
main: canceling other thread
main: joining with other thread
other thread: finished doing thing that can't be canceled
other thread: exiting
main: canceled other thread
main: exiting
However, sleep
gets interrupted and the output is as follows:
main: created other thread
main: letting other thread work for 3 seconds...
other thread: started
other thread: started doing thing that can't be canceled
main: canceling other thread
main: joining with other thread
sleep: Interrupted system call
other thread: finished doing thing that can't be canceled
other thread: exiting
main: canceled other thread
main: exiting
This just seems wrong. Cancellation request is not a signal, right? Then why does it cause sleep
to be interrupted? I expected the code to execute as if instead of just sleep(10)
there was a following code:
// before `thread`:
static pthread_mutex_t sleep_mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sleep_cv = PTHREAD_COND_INITIALIZER;
static void *sleep_thread(void *arg)
{
int sleep_for = *((int *) arg);
sleep(sleep_for);
pthread_cond_broadcast(&sleep_cv);
return NULL;
}
static void *thread(void *arg)
{
// ...
// instead of `sleep`:
pthread_mutex_lock(&sleep_mut);
pthread_t sleep_thr;
int sleep_for = 10;
pthread_create(&sleep_thr, NULL, sleep_thread, &sleep_for);
pthread_cond_wait(&sleep_cv, &sleep_mut);
pthread_mutex_unlock(&sleep_mut);
// ...
}
答案1
得分: 1
根据评论指出,实现可能将取消操作实现为信号。在Linux上,这似乎是主要的实现方式(请参阅备注部分)。
要解决这个问题,您应该使用sleep(3)的返回值,在它被信号中断时继续休眠:
int sleep_for = 10;
while (sleep_for) {
errno = 0;
sleep_for = sleep(sleep_for);
if (errno) {
perror("sleep");
}
}
由于使用秒的精度不够,如果确切的工作时间很重要,直接使用nanosleep(2)可能更好。
英文:
As the comments point out, implementations may implement cancellation as a signal. On Linux, this appears to be the primary implementation (see notes section).
To work around this, you should use the return value of sleep(3) to resume sleeping if it's interrupted by a signal:
int sleep_for = 10;
while (sleep_for) {
errno = 0;
sleep_for = sleep(sleep_for);
if (errno) {
perror("sleep");
}
}
Because of the imprecision of using seconds, it might also be better to directly use nanosleep(2) if the exact working time is important.
答案2
得分: 0
Linux实际上确实使用信号来实现取消操作,这解释了一切。
英文:
As I was pointed out in the comments, Linux actually does implement cancellation using a signal. That explains everything.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论