All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arkadiusz Drabczyk <arkadiusz@drabczyk.org>
To: Alejandro Colomar <alx@kernel.org>
Cc: Florian Weimer <fweimer@redhat.com>,
	mtk.manpages@gmail.com, linux-man@vger.kernel.org,
	libc-help@sourceware.org
Subject: Re: signal(7): why does it say that pthread_mutex_lock() and thread_cond_wait() can fail with EINTR?
Date: Thu, 2 Jan 2025 20:50:17 +0100	[thread overview]
Message-ID: <Z3bt-bQIIdWPEHgl@comp..> (raw)
In-Reply-To: <akntzhpuou75xnct7ymvajyqerfd5vpumzpjjqw3wbqyz67nri@grdc4nyaspkw>

On Thu, Jan 02, 2025 at 01:19:38AM +0100, Alejandro Colomar wrote:
> [CC += libc-help]
>
> Hi Arkadiusz,
>
> On Wed, Jan 01, 2025 at 11:20:26PM +0100, Arkadiusz Drabczyk wrote:
> > In man/man7/signal.7 it says:
> >
> > > If a blocked call to one of the following interfaces is interrupted
> > > by a signal handler, then the call is automatically restarted after
> > > the signal handler returns if the SA_RESTART flag was used;
> > > otherwise the call fails with the error EINTR:
> > > (...)
> > > • pthread_mutex_lock(3), pthread_cond_wait(3), and related APIs.
> >
> > I don't understand this, in my experiments neither
> > pthread_mutex_lock() nor pthread_cond_wait() return EINTR even if
> > signal handler was installed without using SA_RESTART flag.
>
> Please show some minimal examples if you can.

Here is the minimal example for pthread_mutex_lock() with all checks:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>

pthread_mutex_t m;

void handle_sigint(int sig)
{
	(void)sig;
	write(STDOUT_FILENO, "SIGINT", 6);
}

void *example_thread(void *arg)
{
	(void)arg;
	int ret = pthread_mutex_lock(&m);
	if (ret) {
		fprintf(stderr, "pthread_mutex_lock: %s\n", strerror(ret));
		return NULL;
	}
	puts("Mutex acquired in thread");
	sleep(3600);
	ret = pthread_mutex_unlock(&m);
	if (ret)
		fprintf(stderr, "pthread_mutex_unlock: %s\n", strerror(ret));

	return NULL;
}

int main(void)
{
	struct sigaction sa;
	pthread_t thread;
	int ret = 0;
	int main_ret = 0;
	int destroy_mutex = 1;

	sa.sa_handler = handle_sigint;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);

	if (sigaction(SIGINT, &sa, NULL) == -1) {
		perror("sigaction");
		return EXIT_FAILURE;
	}

	pthread_mutex_init(&m, NULL);

	ret = pthread_create(&thread, NULL, example_thread, NULL);
	if (ret) {
		fprintf(stderr, "pthread_create: %s\n", strerror(ret));
		main_ret = ret;
		goto out;
	}

	sleep(3);
	printf("Wait for mutex in main thread, send INT to process %jd now\n", (intmax_t) getpid());
	ret = pthread_mutex_lock(&m);
	if (ret) {
		fprintf(stderr, "pthread_mutex_lock: %s\n", strerror(ret));
		main_ret = ret;
		goto join;
	}

	puts("Got mutex in main thread");

	ret = pthread_mutex_unlock(&m);
	if (ret) {
	   fprintf(stderr, "pthread_mutex_unlock: %s\n", strerror(ret));
	   main_ret = ret;
	   destroy_mutex = 0;
	   goto join;
	}

 join:
	ret = pthread_join(thread, NULL);
	if (ret) {
		fprintf(stderr, "pthread_join: %s\n", strerror(ret));
		return EXIT_FAILURE;
	}

 out:
	if (destroy_mutex) {
		ret = pthread_mutex_destroy(&m);
		if (ret) {
			fprintf(stderr, "pthread_mutex_destroy: %s\n",
				strerror(ret));
			return EXIT_FAILURE;
		}
	}
	return main_ret;
}

> Arkadiusz, would you do the honours writing a patch?  Should I?

I could do that but there is one more problem here - most pthread
manpages mention EINTR only to say that they don't return it except
pthread_cond_init.3:

$ find . -name "*pthread*" -print0 | xargs -0 grep EINTR
./man/man3/pthread_atfork.3:.BR EINTR .
./man/man3/pthread_tryjoin_np.3:.BR EINTR .
./man/man3/pthread_cond_init.3:\fBEINTR\fP
./man/man7/pthreads.7:.BR EINTR .

that says:

> EINTR  pthread_cond_timedwait was interrupted by a signal.

It's hard to say if it ever really worked like that because in
man/man7/pthreads.7 it says that no EINTR has been the requirement
since 2001:

> Most pthreads functions return 0 on success, and an error number on
> failure.  The error numbers that can be returned have the same meaning
> as the error numbers returned in errno by conventional system calls
> and C library functions.  Note that the pthreads functions do not set
> errno.  For each of the pthreads functions that can return an error,
> POSIX.1-2001 specifies that the function can never fail with the error
> EINTR.

Today POSIX still says that pthread_cond_timedwait() cannot return
EINTR
https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_cond_clockwait.html
and in my experiments it really doesn't, both with glibc and musl.

pthread_cond_init.3 has been added in 1998 under linuxthread/
directory, maybe the behavior specified by POSIX was different than
the initial design but OTOH it's quite surprising that no one noticed
the bug for so many years, I wonder how many people still check for
EINTR in pthread_cond_timedwait even though it's not necessary (but
harmless).

So I was thinking that "pthread_mutex_lock(3), pthread_cond_wait(3),
and related APIs." line could be completely removed from signal.7
because there is no pthread API that would return EINTR and EINTR
should be removed from the list of valid error codes in
pthread_cond_init.3. Does it sound good?

-- 
Arkadiusz Drabczyk <arkadiusz@drabczyk.org>

  reply	other threads:[~2025-01-02 19:52 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-01 22:20 signal(7): why does it say that pthread_mutex_lock() and thread_cond_wait() can fail with EINTR? Arkadiusz Drabczyk
2025-01-02  0:19 ` Alejandro Colomar
2025-01-02  9:53   ` Florian Weimer
2025-01-02 12:13     ` Alejandro Colomar
2025-01-02 19:50       ` Arkadiusz Drabczyk [this message]
2025-01-03  1:08         ` Alejandro Colomar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Z3bt-bQIIdWPEHgl@comp.. \
    --to=arkadiusz@drabczyk.org \
    --cc=alx@kernel.org \
    --cc=fweimer@redhat.com \
    --cc=libc-help@sourceware.org \
    --cc=linux-man@vger.kernel.org \
    --cc=mtk.manpages@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.