LibC: Fix pthread cancellation

Install SIGCANCEL handler for all threads.

Remove unneeded atomic stores and loads. States are only changed within
the thread itself.

Define pthread_testcancel as a macro so it gets inlined inside
cancellation points
This commit is contained in:
2026-04-06 19:30:01 +03:00
parent 3fb903d991
commit 7fb27b16e8
2 changed files with 28 additions and 24 deletions

View File

@@ -49,7 +49,7 @@ struct uthread
int errno_; int errno_;
int cancel_type; int cancel_type;
int cancel_state; int cancel_state;
int canceled; volatile int canceled;
// FIXME: make this dynamic // FIXME: make this dynamic
uintptr_t dtv[1 + 256]; uintptr_t dtv[1 + 256];
}; };
@@ -219,6 +219,14 @@ void pthread_testcancel(void);
void pthread_cleanup_pop(int execute); void pthread_cleanup_pop(int execute);
void pthread_cleanup_push(void (*routine)(void*), void* arg); void pthread_cleanup_push(void (*routine)(void*), void* arg);
#define _pthread_testcancel() do { \
struct uthread* uthread = _get_uthread(); \
if (__builtin_expect(uthread->cancel_state == PTHREAD_CANCEL_ENABLE, 1)) \
if (__builtin_expect(uthread->canceled, 0)) \
pthread_exit(PTHREAD_CANCELED); \
} while (0)
#define pthread_testcancel() _pthread_testcancel()
__END_DECLS __END_DECLS
#endif #endif

View File

@@ -22,6 +22,22 @@ struct pthread_trampoline_info_t
void* arg; void* arg;
}; };
static void _pthread_cancel_handler(int)
{
uthread* uthread = _get_uthread();
uthread->canceled = true;
if (uthread->cancel_state == PTHREAD_CANCEL_DISABLE)
return;
if (uthread->cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_exit(PTHREAD_CANCELED);
}
__attribute__((constructor))
static void _install_main_thread_cancel_handler()
{
signal(SIGCANCEL, &_pthread_cancel_handler);
}
// stack is 16 byte aligned on entry, this `call` is used to align it // stack is 16 byte aligned on entry, this `call` is used to align it
extern "C" void _pthread_trampoline(void*); extern "C" void _pthread_trampoline(void*);
asm( asm(
@@ -54,6 +70,7 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
#error #error
#endif #endif
free(arg); free(arg);
signal(SIGCANCEL, &_pthread_cancel_handler);
pthread_exit(info.start_routine(info.arg)); pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@@ -610,25 +627,8 @@ int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(voi
return 0; return 0;
} }
static void pthread_cancel_handler(int)
{
uthread* uthread = _get_uthread();
BAN::atomic_store(uthread->canceled, true);
if (BAN::atomic_load(uthread->cancel_state) != PTHREAD_CANCEL_ENABLE)
return;
switch (BAN::atomic_load(uthread->cancel_type))
{
case PTHREAD_CANCEL_ASYNCHRONOUS:
pthread_exit(PTHREAD_CANCELED);
case PTHREAD_CANCEL_DEFERRED:
return;
}
ASSERT_NOT_REACHED();
}
int pthread_cancel(pthread_t thread) int pthread_cancel(pthread_t thread)
{ {
signal(SIGCANCEL, &pthread_cancel_handler);
return pthread_kill(thread, SIGCANCEL); return pthread_kill(thread, SIGCANCEL);
} }
@@ -666,14 +666,10 @@ int pthread_setcanceltype(int type, int* oldtype)
return 0; return 0;
} }
#undef pthread_testcancel
void pthread_testcancel(void) void pthread_testcancel(void)
{ {
uthread* uthread = _get_uthread(); _pthread_testcancel();
if (BAN::atomic_load(uthread->cancel_state) != PTHREAD_CANCEL_ENABLE)
return;
if (!BAN::atomic_load(uthread->canceled))
return;
pthread_exit(PTHREAD_CANCELED);
} }
int pthread_getschedparam(pthread_t thread, int* __restrict policy, struct sched_param* __restrict param) int pthread_getschedparam(pthread_t thread, int* __restrict policy, struct sched_param* __restrict param)