21#include <sys/syscall.h>
26enum { wait_timeout_seconds = 2 };
37 enum io_operation operation;
39 int rights_descriptor;
41 volatile int returned;
49 struct cmsghdr alignment;
50 unsigned char bytes[CMSG_SPACE(
sizeof(
int))];
53static int socket_type = SOCK_STREAM;
54static volatile sig_atomic_t signal_calls;
55static volatile sig_atomic_t close_from_signal = -1;
56static volatile sig_atomic_t signal_close_result = -2;
58static void signal_handler(
int signal_number) {
59 if (signal_number == SIGUSR1) {
61 const int descriptor = __atomic_exchange_n(&close_from_signal, -1, __ATOMIC_RELAXED);
62 if (descriptor >= 0) {
63 __atomic_store_n(&signal_close_result, close(descriptor), __ATOMIC_RELEASE);
68static void* run_io(
void* parameter) {
71 context->tid = syscall(SYS_gettid);
72 context->tid_error = context->tid < 0 ? errno : 0;
74 __atomic_store_n(&context->entered, 1, __ATOMIC_RELEASE);
75 if (context->use_message) {
76 struct iovec vector = {
77 .iov_base = context->payload,
78 .iov_len = context->length,
84 .msg_control = control.bytes,
85 .msg_controllen =
sizeof(control.bytes),
87 if (context->operation == send_operation) {
88 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
89 header->cmsg_len = CMSG_LEN(
sizeof(
int));
90 header->cmsg_level = SOL_SOCKET;
91 header->cmsg_type = SCM_RIGHTS;
92 memcpy(CMSG_DATA(header), &context->rights_descriptor,
sizeof(context->rights_descriptor));
93 context->result = sendmsg(context->descriptor, &
message, MSG_NOSIGNAL);
95 context->result = recvmsg(context->descriptor, &
message, 0);
97 }
else if (context->operation == receive_operation) {
98 context->result = recv(context->descriptor, context->payload, context->length, 0);
100 context->result = send(context->descriptor, context->payload, context->length, MSG_NOSIGNAL);
102 context->error = errno;
103 __atomic_store_n(&context->returned, 1, __ATOMIC_RELEASE);
107static int monotonic_now(
struct timespec* now) {
108 if (clock_gettime(CLOCK_MONOTONIC, now)) {
109 fprintf(stderr,
"AF_UNIX interruption clock_gettime failed: errno=%d\n", errno);
115static int deadline_passed(
const struct timespec* deadline) {
117 if (monotonic_now(&now)) {
120 return now.tv_sec > deadline->tv_sec ||
121 (now.tv_sec == deadline->tv_sec && now.tv_nsec >= deadline->tv_nsec);
124static int wait_for_value(
volatile int* value) {
125 struct timespec deadline;
126 if (monotonic_now(&deadline)) {
129 deadline.tv_sec += wait_timeout_seconds;
130 while (!__atomic_load_n(value, __ATOMIC_ACQUIRE)) {
131 const int expired = deadline_passed(&deadline);
135 fprintf(stderr,
"AF_UNIX interruption state wait timed out\n");
144static int install_signal_handler(
void) {
145 struct sigaction action = {0};
147 __atomic_store_n(&close_from_signal, -1, __ATOMIC_RELAXED);
148 __atomic_store_n(&signal_close_result, -2, __ATOMIC_RELAXED);
149 action.sa_handler = signal_handler;
150 return sigemptyset(&action.sa_mask) || sigaction(SIGUSR1, &action, 0) ||
151 signal(SIGPIPE, SIG_IGN) == SIG_ERR;
154static int interrupt_worker(
struct io_context* context) {
155 struct timespec deadline;
156 if (monotonic_now(&deadline)) {
159 deadline.tv_sec += wait_timeout_seconds;
160 for (
size_t attempt = 0;; ++attempt) {
161 if (__atomic_load_n(&context->returned, __ATOMIC_ACQUIRE)) {
164 if (deadline_passed(&deadline)) {
165 fprintf(stderr,
"AF_UNIX interruption worker timed out: tid=%ld attempts=%zu\n", context->tid,
169 if (syscall(SYS_tkill, context->tid, SIGUSR1)) {
170 const int error = errno;
173 if (__atomic_load_n(&context->returned, __ATOMIC_ACQUIRE)) {
176 fprintf(stderr,
"AF_UNIX interruption tkill failed: tid=%ld errno=%d\n", context->tid, error);
181 for (
size_t pause = 0; pause < 32; ++pause) {
182 if (__atomic_load_n(&context->returned, __ATOMIC_ACQUIRE)) {
188 fprintf(stderr,
"AF_UNIX interruption initial yield budget exhausted: tid=%ld\n",
194 const struct timespec pause = {.tv_nsec = 10000000};
195 if (nanosleep(&pause, NULL) && errno != EINTR) {
196 fprintf(stderr,
"AF_UNIX interruption retry sleep failed: errno=%d\n", errno);
203static int receive_stage_failed(
const char* stage,
int error,
const struct io_context* context) {
204 const int entered = __atomic_load_n(&context->entered, __ATOMIC_ACQUIRE);
205 const int returned = __atomic_load_n(&context->returned, __ATOMIC_ACQUIRE);
207 "AF_UNIX interrupted receive failed: stage=%s error=%d tid=%ld tid_errno=%d "
208 "entered=%d returned=%d result=%ld io_errno=%d\n",
209 stage, error, entered ? context->tid : 0, entered ? context->tid_error : 0, entered,
210 returned, (long)(returned ? context->result : -2), returned ? context->error : 0);
214static int fill_send_queue(
int descriptor,
const char* payload,
size_t length) {
215 const int flags = fcntl(descriptor, F_GETFL);
216 if (flags < 0 || fcntl(descriptor, F_SETFL, flags | O_NONBLOCK))
222 const ssize_t result = send(descriptor, payload, length, MSG_NOSIGNAL);
224 written += (size_t)result;
227 if (result != -1 || (errno != EAGAIN && errno != EWOULDBLOCK))
232 return written && !fcntl(descriptor, F_SETFL, flags) ? 0 : -1;
235static int interrupted_receive_child(
void) {
237 if (install_signal_handler())
242 if (socketpair(AF_UNIX, socket_type, 0, sockets)) {
243 fprintf(stderr,
"interrupted receive socketpair failed: errno=%d\n", errno);
248 .descriptor = sockets[1],
251 .operation = receive_operation,
255 int error = pthread_create(&
worker, 0, run_io, &context);
257 return receive_stage_failed(
"pthread_create", error, &context);
259 if (wait_for_value(&context.entered)) {
260 return receive_stage_failed(
"worker_entered", errno, &context);
262 if (context.tid <= 0) {
263 return receive_stage_failed(
"worker_tid", context.tid_error, &context);
265 if (interrupt_worker(&context)) {
266 return receive_stage_failed(
"interrupt_worker", errno, &context);
268 error = pthread_join(
worker, 0);
270 return receive_stage_failed(
"pthread_join", error, &context);
273 if (context.result != -1 || context.error != EINTR || signal_calls < 1)
275 return close(sockets[0]) || close(sockets[1]) ? 14 : 0;
278static int partial_send_child(
void) {
280 if (install_signal_handler())
285 memset(payload,
'q',
sizeof(payload));
286 if (socketpair(AF_UNIX, socket_type, 0, sockets) ||
287 fill_send_queue(sockets[0], payload,
sizeof(payload)) || recv(sockets[1], payload, 1, 0) != 1)
293 .descriptor = sockets[0],
296 .operation = send_operation,
300 if (pthread_create(&
worker, 0, run_io, &context) || wait_for_value(&context.entered) ||
301 context.tid <= 0 || interrupt_worker(&context) || pthread_join(
worker, 0))
304 if (context.result != 1 || context.error || signal_calls < 1)
306 return close(sockets[0]) || close(sockets[1]) ? 24 : 0;
309static int interrupted_send_child(
void) {
311 if (install_signal_handler())
316 memset(payload,
'i',
sizeof(payload));
317 if (socketpair(AF_UNIX, socket_type, 0, sockets) ||
318 fill_send_queue(sockets[0], payload,
sizeof(payload)))
322 .descriptor = sockets[0],
325 .operation = send_operation,
329 if (pthread_create(&
worker, 0, run_io, &context) || wait_for_value(&context.entered) ||
330 context.tid <= 0 || interrupt_worker(&context) || pthread_join(
worker, 0))
333 if (context.result != -1 || context.error != EINTR || signal_calls < 1)
335 return close(sockets[0]) || close(sockets[1]) ? 29 : 0;
338static int signal_handler_close_child(
enum io_operation operation) {
340 if (install_signal_handler())
345 memset(payload,
'h',
sizeof(payload));
346 if (socketpair(AF_UNIX, socket_type, 0, sockets))
348 if (operation == send_operation && fill_send_queue(sockets[0], payload,
sizeof(payload)))
351 int rights_pipe[2] = {-1, -1};
352 if (operation == send_operation && pipe(rights_pipe))
355 const int operation_side = operation == receive_operation ? 1 : 0;
356 const int close_side = operation == receive_operation ? operation_side : 1 - operation_side;
358 .descriptor = sockets[operation_side],
361 .operation = operation,
363 .rights_descriptor = rights_pipe[0],
367 if (pthread_create(&
worker, 0, run_io, &context) || wait_for_value(&context.entered))
369 for (
size_t pause = 0; pause < 512; ++pause)
372 __atomic_store_n(&close_from_signal, sockets[close_side], __ATOMIC_RELEASE);
373 if (__atomic_load_n(&context.returned, __ATOMIC_ACQUIRE) || context.tid <= 0 ||
374 interrupt_worker(&context) || pthread_join(
worker, 0))
376 if (__atomic_load_n(&signal_close_result, __ATOMIC_ACQUIRE) || context.result != -1 ||
377 context.error != EINTR || signal_calls < 1)
380 sockets[close_side] = -1;
381 if (sockets[0] >= 0 && close(sockets[0]))
383 if (sockets[1] >= 0 && close(sockets[1]))
385 if (rights_pipe[0] >= 0 && close(rights_pipe[0]))
387 if (rights_pipe[1] >= 0 && close(rights_pipe[1]))
392static int receive_signal_handler_close_child(
void) {
393 return signal_handler_close_child(receive_operation);
396static int send_signal_handler_close_child(
void) {
397 return signal_handler_close_child(send_operation);
400static int serialized_wait_child(
enum io_operation operation) {
402 if (install_signal_handler())
407 memset(payload,
's',
sizeof(payload));
408 if (socketpair(AF_UNIX, socket_type, 0, sockets))
410 if (operation == send_operation && fill_send_queue(sockets[0], payload,
sizeof(payload)))
413 const int operation_side = operation == receive_operation ? 1 : 0;
415 .descriptor = sockets[operation_side],
418 .operation = operation,
422 .descriptor = sockets[operation_side],
425 .operation = operation,
428 pthread_t holder_thread;
429 pthread_t waiter_thread;
430 if (pthread_create(&holder_thread, 0, run_io, &holder) || wait_for_value(&holder.entered))
432 for (
size_t pause = 0; pause < 512; ++pause)
434 if (__atomic_load_n(&holder.returned, __ATOMIC_ACQUIRE) ||
435 pthread_create(&waiter_thread, 0, run_io, &
waiter) || wait_for_value(&
waiter.entered) ||
436 waiter.tid <= 0 || interrupt_worker(&
waiter) || pthread_join(waiter_thread, 0))
439 if (
waiter.result != -1 ||
waiter.error != EINTR || signal_calls < 1 ||
440 __atomic_load_n(&holder.returned, __ATOMIC_ACQUIRE))
442 if (close(sockets[0]) || close(sockets[1]) || pthread_join(holder_thread, 0))
445 const int holder_result_ok = operation == receive_operation
447 : holder.result == -1 && holder.error == EPIPE;
448 return holder_result_ok ? 0 : 47;
451static int serialized_receive_wait_child(
void) {
452 return serialized_wait_child(receive_operation);
455static int serialized_send_wait_child(
void) {
456 return serialized_wait_child(send_operation);
459static int close_wake_child(
enum io_operation operation,
int close_local) {
461 if (install_signal_handler())
466 memset(payload,
'c',
sizeof(payload));
467 if (socketpair(AF_UNIX, socket_type, 0, sockets))
469 if (operation == send_operation && fill_send_queue(sockets[0], payload,
sizeof(payload)))
472 const int operation_side = operation == receive_operation ? 1 : 0;
473 const int close_side = close_local ? operation_side : 1 - operation_side;
475 .descriptor = sockets[operation_side],
478 .operation = operation,
482 if (pthread_create(&
worker, 0, run_io, &context) || wait_for_value(&context.entered))
484 for (
size_t pause = 0; pause < 256; ++pause)
487 if (close(sockets[close_side]))
489 sockets[close_side] = -1;
490 const int returned_without_rescue = !wait_for_value(&context.returned);
491 if (!returned_without_rescue) {
496 (void)syscall(SYS_tkill, context.tid, SIGUSR1);
498 if (pthread_join(
worker, 0))
501 const int result_ok = operation == receive_operation
502 ? context.result == 0
503 : context.result == -1 && context.error == EPIPE;
504 if (!returned_without_rescue || !result_ok)
506 if (sockets[0] >= 0 && close(sockets[0]))
508 if (sockets[1] >= 0 && close(sockets[1]))
513static void run_bounded(
int (*child_test)(
void)) {
514 const pid_t child = fork();
523 waited = waitpid(child, &status_code, 0);
524 }
while (waited < 0 && errno == EINTR);
525 if (waited != child || !WIFEXITED(status_code) || WEXITSTATUS(status_code)) {
526 if (waited == child && WIFEXITED(status_code))
527 fprintf(stderr,
"AF_UNIX interruption child failed: code=%d\n", WEXITSTATUS(status_code));
532static int peer_receive_close_child(
void) {
533 return close_wake_child(receive_operation, 0);
536static int local_receive_close_child(
void) {
537 return close_wake_child(receive_operation, 1);
540static int peer_send_close_child(
void) {
541 return close_wake_child(send_operation, 0);
544static int local_send_close_child(
void) {
545 return close_wake_child(send_operation, 1);
548static void test_unix_interruption(
int type) {
550 printf(
"Testing AF_UNIX %s interruption and close wakeups... \n",
551 type == SOCK_STREAM ?
"stream" :
"sequenced-packet");
553 run_bounded(interrupted_receive_child);
554 run_bounded(interrupted_send_child);
555 run_bounded(receive_signal_handler_close_child);
556 run_bounded(send_signal_handler_close_child);
557 run_bounded(serialized_receive_wait_child);
558 run_bounded(serialized_send_wait_child);
559 if (type == SOCK_STREAM) {
560 run_bounded(partial_send_child);
562 run_bounded(peer_receive_close_child);
563 run_bounded(local_receive_close_child);
564 run_bounded(peer_send_close_child);
565 run_bounded(local_send_close_child);
566 puts(type == SOCK_STREAM ?
"OK\n" :
"SEQPACKET-INTERRUPTION-PASS");
570void test_unix_stream_interruption(
void) {
571 test_unix_interruption(SOCK_STREAM);
574void test_unix_seqpacket_interruption(
void) {
575 test_unix_interruption(SOCK_SEQPACKET);