From: Akira Yokosawa <akiyks@gmail.com>
To: paulmck@linux.vnet.ibm.com
Cc: Elad Lahav <e2lahav@gmail.com>,
perfbook@vger.kernel.org, Akira Yokosawa <akiyks@gmail.com>
Subject: Re: Section 4.2: wrong error reporting for pthread functions
Date: Tue, 17 Jul 2018 00:42:57 +0900 [thread overview]
Message-ID: <4131cfaa-45c2-e0e0-ecc0-dfa3058c53c3@gmail.com> (raw)
In-Reply-To: <20180714233313.GH12945@linux.vnet.ibm.com>
Hi Paul,
See inline comments below for a few nits and suggestions.
On 2018/07/14 16:33:13 -0700, Paul E. McKenney wrote:
> On Sat, Jul 14, 2018 at 08:59:48AM -0400, Elad Lahav wrote:
>> Hello,
>>
>> Throughout section 4.2 the following pattern is used for reporting
>> errors returned by various pthread functions:
>>
>> if (pthread_func() == 0) {
>> perror("pthread_func");
>> exit(-1);
>> }
>>
>> This is wrong, as pthread functions return the error number on failure
>> and do not set errno, as demonstrated by the following program, which
>> attempts to join with a detached thread:
>>
>> #include <stdio.h>
>> #include <errno.h>
>> #include <pthread.h>
>>
>> static void *
>> my_thread(void *arg)
>> {
>> return NULL;
>> }
>>
>> int
>> main()
>> {
>> pthread_attr_t attr;
>> pthread_t tid;
>>
>> pthread_attr_init(&attr);
>> pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
>>
>> errno = 0;
>>
>> if (pthread_create(&tid, &attr, my_thread, NULL) != 0) {
>> perror("pthread_create");
>> return 1;
>> }
>>
>> if (pthread_join(tid, NULL) != 0) {
>> perror("pthread_join");
>> return 1;
>> }
>>
>> return 0;
>> }
>>
>> The result:
>> # ./pthread_error
>> pthread_join: Success
>>
>> The correct way to report errors is to use strerror() on the returned code:
>>
>> #include <string.h>
>> ...
>> int rc;
>> rc = pthread_join(tid, NULL);
>> if (rc != 0) {
>> fprintf(stderr, "pthread_join: %s\n", strerror(rc));
>> return 1;
>> }
>>
>> # ./pthread_error
>> pthread_join: Invalid argument
>
> Huh. I haven't seen a failure.
>
> But you are right, the documentation clearly states that these calls
> return zero for success or errno otherwise. Some systems explicitly
> say that errno is unaffected, others guarantee setting errno as I was
> naively expecting. The standard is silent on the treatment of errno.
> Some people suggest assigning the return value of the pthread_*()
> functions to errno, while others argue that any use whatsoever of errno
> is signal-unsafe (looks like saving and restoring errno in your signal
> handlers is the right approach, which is a timely reminder for me).
>
> Well, portability is worth a few lines of code, so I will make these
> changes with your Reported-by.
>
> Just out of curiosity, did you find these by inspection, or do they
> actually fail in your environment?
>
>> Also, using exit(-1) is likely to produce unexpected results for the
>> parent process. Error codes returned from processes are small
>> integers, with the range being platform-specific. This means that -1
>> will get truncated to some unspecified value. It is customary to use 1
>> to indicate a failure from a process (if no other specific code is
>> defined). stdlib.h provides EXIT_SUCCESS and EXIT_FAILURE, which may
>> be easier to read.
>
> Also a fair point.
>
> And yes, I did start C programming long before there was such a thing
> as stdlib.h. Why do you ask? ;-)
>
> Please see below for the first of a number of patches. Thoughts?
>
> Thanx, Paul
>
> ------------------------------------------------------------------------
>
> commit 2568586a3b09fa8f497e724e0e31ba020a37275a
> Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
> Date: Sat Jul 14 16:29:34 2018 -0700
>
> toolsoftrade: Standard pthread error handling and exit() arguments
>
> This commit uses the pthread library calls' return value to do error
> checking and handling, instead of the old reliance on errno, which for
> these functions is not guaranteed by the relevant standards. This commit
> also uses EXIT_SUCCESS and EXIT_FAILURE to indicate exit status.
>
> Reported-by: Elad Lahav <e2lahav@gmail.com>
> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
>
> diff --git a/CodeSamples/toolsoftrade/forkjoin.c b/CodeSamples/toolsoftrade/forkjoin.c
> index d578221f6f40..b03b545f2987 100644
> --- a/CodeSamples/toolsoftrade/forkjoin.c
> +++ b/CodeSamples/toolsoftrade/forkjoin.c
> @@ -34,7 +34,7 @@ int main(int argc, char *argv[])
>
> if (argc != 2) {
> fprintf(stderr, "Usage: %s nforks\n", argv[0]);
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> nforks = atoi(argv[1]);
> printf("%d fork()s\n", nforks);
> @@ -52,7 +52,7 @@ int main(int argc, char *argv[])
> }
> if (pid < 0) { /* parent, upon error */
> perror("fork");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> }
>
> @@ -66,5 +66,5 @@ int main(int argc, char *argv[])
> fprintf(stderr, "system(\"date\") failed: %x\n", stat_val);
> }
>
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/forkjoinperf.c b/CodeSamples/toolsoftrade/forkjoinperf.c
> index c1438886e58a..19bad3e19acb 100644
> --- a/CodeSamples/toolsoftrade/forkjoinperf.c
> +++ b/CodeSamples/toolsoftrade/forkjoinperf.c
> @@ -34,7 +34,7 @@ int main(int argc, char *argv[])
>
> if (argc != 2) {
> fprintf(stderr, "Usage: %s nforks\n", argv[0]);
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> nforks = atoi(argv[1]);
> printf("%d fork()s\n", nforks);
> @@ -44,11 +44,11 @@ int main(int argc, char *argv[])
> for (i = 0; i < nforks; i++) {
> pid = fork();
> if (pid == 0) { /* child */
> - exit(0);
> + exit(EXIT_SUCCESS);
> }
> if (pid < 0) { /* parent, upon error */
> perror("fork");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> for (;;) {
> pid = wait(&status);
> @@ -56,7 +56,7 @@ int main(int argc, char *argv[])
> if (errno == ECHILD)
> break;
> perror("wait");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> }
> }
> @@ -64,5 +64,5 @@ int main(int argc, char *argv[])
> fflush(stdout);
> i = system("date");
>
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/forkjoinvar.c b/CodeSamples/toolsoftrade/forkjoinvar.c
> index 638558b726db..571acc14d8db 100644
> --- a/CodeSamples/toolsoftrade/forkjoinvar.c
> +++ b/CodeSamples/toolsoftrade/forkjoinvar.c
> @@ -34,11 +34,11 @@ int main(int argc, char *argv[])
> if (pid == 0) { /* child */
> x = 1;
> printf("Child process set x=1\n");
> - exit(0);
> + exit(EXIT_SUCCESS);
> }
> if (pid < 0) { /* parent, upon error */
> perror("fork");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
>
> /* parent */
> @@ -46,5 +46,5 @@ int main(int argc, char *argv[])
> waitall();
> printf("Parent process sees x=%d\n", x);
>
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/lock.c b/CodeSamples/toolsoftrade/lock.c
> index c5ad6ebf37f3..6bb3c23b43ba 100644
> --- a/CodeSamples/toolsoftrade/lock.c
> +++ b/CodeSamples/toolsoftrade/lock.c
> @@ -33,14 +33,16 @@ int x = 0;
>
> void *lock_reader(void *arg)
> {
> + int en;
> int i;
> int newx = -1;
> int oldx = -1;
> pthread_mutex_t *pmlp = (pthread_mutex_t *)arg;
>
> - if (pthread_mutex_lock(pmlp) != 0) {
> - perror("lock_reader:pthread_mutex_lock");
> - exit(-1);
> + if ((en = pthread_mutex_lock(pmlp)) != 0) {
> + fprintf(stderr, "lock_reader:pthread_mutex_lock: %s\n",
> + strerror(en));
> + exit(EXIT_FAILURE);
> }
> for (i = 0; i < 100; i++) {
> newx = READ_ONCE(x);
> @@ -50,75 +52,80 @@ void *lock_reader(void *arg)
> oldx = newx;
> poll(NULL, 0, 1);
> }
> - if (pthread_mutex_unlock(pmlp) != 0) {
> - perror("lock_reader:pthread_mutex_unlock");
> - exit(-1);
> + if ((en = pthread_mutex_unlock(pmlp)) != 0) {
> + fprintf(stderr, "lock_reader:pthread_mutex_lock: %s\n",
> + strerror(en));
> + exit(EXIT_FAILURE);
> }
> return NULL;
> }
>
> void *lock_writer(void *arg)
> {
> + int en;
> int i;
> pthread_mutex_t *pmlp = (pthread_mutex_t *)arg;
>
> - if (pthread_mutex_lock(pmlp) != 0) {
> - perror("lock_writer:pthread_mutex_lock");
> - exit(-1);
> + if ((en = pthread_mutex_lock(pmlp)) != 0) {
> + fprintf(stderr, "lock_writer:pthread_mutex_lock: %s\n",
> + strerror(en));
> + exit(EXIT_FAILURE);
> }
> for (i = 0; i < 3; i++) {
> WRITE_ONCE(x, READ_ONCE(x) + 1);
> poll(NULL, 0, 5);
> }
> - if (pthread_mutex_unlock(pmlp) != 0) {
> - perror("lock_writer:pthread_mutex_unlock");
> - exit(-1);
> + if ((en = pthread_mutex_unlock(pmlp)) != 0) {
> + fprintf(stderr, "lock_writer:pthread_mutex_lock: %s\n",
> + strerror(en));
> + exit(EXIT_FAILURE);
> }
> return NULL;
> }
>
> int main(int argc, char *argv[])
> {
> + int en;
> pthread_t tid1;
> pthread_t tid2;
> void *vp;
>
> printf("Creating two threads using same lock:\n");
> - if (pthread_create(&tid1, NULL, lock_reader, &lock_a) != 0) {
> - perror("pthread_create");
> - exit(-1);
> + if ((en = pthread_create(&tid1, NULL, lock_reader, &lock_a)) != 0) {
> + fprintf(stderr, "pthread_create: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_create(&tid2, NULL, lock_writer, &lock_a) != 0) {
> - perror("pthread_create");
> - exit(-1);
> + if ((en = pthread_create(&tid2, NULL, lock_writer, &lock_a)) != 0) {
> + fprintf(stderr, "pthread_create: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_join(tid1, &vp) != 0) {
> - perror("pthread_join");
> - exit(-1);
> + if ((en = pthread_join(tid1, &vp)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_join(tid2, &vp) != 0) {
> - perror("pthread_join");
> - exit(-1);
> + if ((en = pthread_join(tid2, &vp)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
>
> printf("Creating two threads w/different locks:\n");
> x = 0;
> - if (pthread_create(&tid1, NULL, lock_reader, &lock_a) != 0) {
> - perror("pthread_create");
> - exit(-1);
> + if ((en = pthread_create(&tid1, NULL, lock_reader, &lock_a)) != 0) {
> + fprintf(stderr, "pthread_create: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_create(&tid2, NULL, lock_writer, &lock_b) != 0) {
> - perror("pthread_create");
> - exit(-1);
> + if ((en = pthread_create(&tid2, NULL, lock_writer, &lock_b)) != 0) {
> + fprintf(stderr, "pthread_create: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_join(tid1, &vp) != 0) {
> - perror("pthread_join");
> - exit(-1);
> + if ((en = pthread_join(tid1, &vp)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> - if (pthread_join(tid2, &vp) != 0) {
> - perror("pthread_join");
> - exit(-1);
> + if ((en = pthread_join(tid2, &vp)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
>
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/mytrue.c b/CodeSamples/toolsoftrade/mytrue.c
> index a6ee655c802c..59cbe48d32d3 100644
> --- a/CodeSamples/toolsoftrade/mytrue.c
> +++ b/CodeSamples/toolsoftrade/mytrue.c
> @@ -23,5 +23,5 @@
>
> int main(int argc, char *argv[])
> {
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/pcreate.c b/CodeSamples/toolsoftrade/pcreate.c
> index feca73c4faf6..5241f8f96c76 100644
> --- a/CodeSamples/toolsoftrade/pcreate.c
> +++ b/CodeSamples/toolsoftrade/pcreate.c
> @@ -37,21 +37,22 @@ void *mythread(void *arg)
>
> int main(int argc, char *argv[])
> {
> + int en;
> pthread_t tid;
> void *vp;
>
> - if (pthread_create(&tid, NULL, mythread, NULL) != 0) {
> - perror("pthread_create");
> - exit(-1);
> + if ((en = pthread_create(&tid, NULL, mythread, NULL)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
>
> /* parent */
>
> - if (pthread_join(tid, &vp) != 0) {
> - perror("pthread_join");
> - exit(-1);
> + if ((en = pthread_join(tid, &vp)) != 0) {
> + fprintf(stderr, "pthread_join: %s\n", strerror(en));
> + exit(EXIT_FAILURE);
> }
> printf("Parent process sees x=%d\n", x);
>
> - return 0;
> + return EXIT_SUCCESS;
> }
> diff --git a/CodeSamples/toolsoftrade/rwlockscale.c b/CodeSamples/toolsoftrade/rwlockscale.c
> index 1bf487e7099c..136b799a160b 100644
> --- a/CodeSamples/toolsoftrade/rwlockscale.c
> +++ b/CodeSamples/toolsoftrade/rwlockscale.c
> @@ -39,6 +39,7 @@ char goflag = GOFLAG_INIT;
>
> void *reader(void *arg)
> {
> + int en;
> int i;
> long long loopcnt = 0;
> long me = (long)arg;
> @@ -48,16 +49,16 @@ void *reader(void *arg)
> continue;
> }
> while (READ_ONCE(goflag) == GOFLAG_RUN) {
> - if (pthread_rwlock_rdlock(&rwl) != 0) {
> + if ((en = pthread_rwlock_rdlock(&rwl)) != 0) {
> perror("pthread_rwlock_rdlock");
This perror() also needs update.
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> for (i = 1; i < holdtime; i++) {
> barrier();
> }
> - if (pthread_rwlock_unlock(&rwl) != 0) {
> + if ((en = pthread_rwlock_unlock(&rwl)) != 0) {
> perror("pthread_rwlock_unlock");
Ditto.
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> for (i = 1; i < thinktime; i++) {
> barrier();
> @@ -70,6 +71,7 @@ void *reader(void *arg)
>
> int main(int argc, char *argv[])
> {
> + int en;
> long i;
> int nthreads;
> long long sum = 0LL;
> @@ -79,7 +81,7 @@ int main(int argc, char *argv[])
> if (argc != 4) {
> fprintf(stderr,
> "Usage: %s nthreads holdloops thinkloops\n", argv[0]);
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> nthreads = strtoul(argv[1], NULL, 0);
> holdtime = strtoul(argv[2], NULL, 0);
> @@ -88,12 +90,13 @@ int main(int argc, char *argv[])
> tid = malloc(nthreads * sizeof(tid[0]));
> if (readcounts == NULL || tid == NULL) {
> fprintf(stderr, "Out of memory\n");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> for (i = 0; i < nthreads; i++) {
> - if (pthread_create(&tid[i], NULL, reader, (void *)i) != 0) {
> + en = pthread_create(&tid[i], NULL, reader, (void *)i);
> + if (en != 0) {
> perror("pthread_create");
Ditto.
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> }
> while (READ_ONCE(nreadersrunning) < nthreads) {
> @@ -104,9 +107,9 @@ int main(int argc, char *argv[])
> goflag = GOFLAG_STOP;
>
> for (i = 0; i < nthreads; i++) {
> - if (pthread_join(tid[i], &vp) != 0) {
> + if ((en = pthread_join(tid[i], &vp)) != 0) {
> perror("pthread_join");
Ditto.
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> }
> for (i = 0; i < nthreads; i++) {
> @@ -116,5 +119,5 @@ int main(int argc, char *argv[])
> printf("%s n: %d h: %d t: %d sum: %lld\n",
> argv[0], nthreads, holdtime, thinktime, sum);
>
> - return 0;
> + return EXIT_SUCCESS;
> }
>
> --
> To unsubscribe from this list: send the line "unsubscribe perfbook" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
I see you have already updated most of the code samples under CodeSamples/,
but let me suggest an alternative way not to increase line counts
(or even to decrease line counts).
"pthread_create(3)" man page gives you an example code.
First, two helpers are defined as follows:
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
Then, one of the call sites looks as follows:
s = pthread_create(&tinfo[tnum].thread_id, &attr,
&thread_start, &tinfo[tnum]);
if (s != 0)
handle_error_en(s, "pthread_create");
If we employ this pattern, one of the hunks in your patch will look like:
- if (pthread_mutex_lock(pmlp) != 0) {
- perror("lock_reader:pthread_mutex_lock");
- exit(-1);
- }
+ if ((en = pthread_mutex_lock(pmlp)) != 0)
+ handle_error_en(en, "lock_reader:pthread_mutex_lock");
Thoughts?
I think these error cases are not our main topic, and to hide the
details inside helpers sounds reasonable.
Also, wouldn't it be a good idea to employ auto-numbering scheme as
mentioned in Section D.3.1.1 of Style Guide when updating code snippets?
This update will involve a lot of renumbering of line numbers otherwise.
If you feel OK with this approach, I can prepare a patch series
on behalf of you. (Can take a little while, though.)
Thanks, Akira
next prev parent reply other threads:[~2018-07-16 15:42 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-07-14 12:59 Section 4.2: wrong error reporting for pthread functions Elad Lahav
2018-07-14 23:33 ` Paul E. McKenney
2018-07-16 15:42 ` Akira Yokosawa [this message]
2018-07-16 16:39 ` Paul E. McKenney
2018-07-17 15:43 ` Akira Yokosawa
2018-07-17 16:15 ` Paul E. McKenney
2018-07-18 12:15 ` Akira Yokosawa
2018-07-18 13:14 ` Paul E. McKenney
2018-07-18 13:42 ` Akira Yokosawa
2018-07-18 18:44 ` Paul E. McKenney
[not found] ` <CAJbg=FW7vpepT_0wq+ffpQBHgKX+s1ruDb5Bs-m3FCPHKcPV3A@mail.gmail.com>
2018-07-16 23:51 ` Akira Yokosawa
2018-07-17 11:05 ` Elad Lahav
2018-07-17 16:27 ` Paul E. McKenney
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=4131cfaa-45c2-e0e0-ecc0-dfa3058c53c3@gmail.com \
--to=akiyks@gmail.com \
--cc=e2lahav@gmail.com \
--cc=paulmck@linux.vnet.ibm.com \
--cc=perfbook@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox