Subject: man page for robust mutexes

Robust mutexes are available since a few days, so here is a man page for
them. I used pthread_mutexattr_setprotocol.3p as a template and sneaked a
few phrases from there. That's my first man-page however, so some things
might be wrong :)

Sebastian
--- /dev/null
+++ b/man3p/pthread_mutexattr_setrobust_np.3p
@@ -0,0 +1,240 @@
+.\" Copyright (c) 2008 Sebastian Andrzej Siewior
+.TH "ROBUST MUTEXES" P 2008 "" "POSIX Programmer's Manual"
+.\" pthread_mutexattr_setrobust_np
+.SH NAME
+pthread_mutexattr_setrobust_np, pthread_mutexattr_getrobust_np \- set or get
+the robustness of a mutex
+.SH SYNOPSIS
+.LP
+\fB#include <pthread.h>
+.br
+.sp
+#define __USE_GNU
+.sp
+int pthread_mutexattr_getrobust_np(pthread_mutexattr_t *\fP\fIattr\fP\fB, int
+\fP\fIrobustness\fP\fB);
+.br
+int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *\fP\fIattr\fP\fB, int
+\fP\fIrobustness\fP\fB);
+.br
+int pthread_mutex_consistent_np(pthread_mutex_t *\fP\fImutex\fP\fB);
+\fP
+\fB
+.br
+\fP
+.SH DESCRIPTION
+.LP
+The \fIpthread_mutexattr_getrobust_np\fP() and
+\fIpthread_mutexattr_setrobust_np\fP() functions, respectively, shall get and
+set the robustness attribute of a mutex attributes object pointed to by
+\fIattr\fP which was previously created by the function
+\fIpthread_mutexattr_init\fP().
+.LP
+The \fIrobustness\fP attribute defines the robustness to be used in utilizing
+mutexes. The value of \fIrobustness\fP may be one of:
+.LP
+.sp
+PTHREAD_MUTEX_STALLED_NP
+.br
+.sp
+PTHREAD_MUTEX_ROBUST_NP
+.br
+.sp
+.LP
+which are defined in the \fI<pthread.h>\fP header if the GNU extensions are
+used.
+.LP
+The default attribute is PTHREAD_MUTEX_STALLED_NP.
+.LP
+When a mutex is created with PTHREAD_MUTEX_STALLED_NP is locked and the owner
+dies, then the next call to pthread_mutex_lock() will block forever. Also,
+the already waiting waiters will wait for ever.
+.LP
+The behavior is different if the mutex is created with
+PTHREAD_MUTEX_ROBUST_NP. If the owner dies while holding the lock, the
+next call to \fIpthread_mutex_lock\fP() will return EOWNERDEAD and the caller
+will acquire lock. The new owner should call
+\fIpthread_mutex_consistent_np\fP() on the mutex once the internal state of
+the protected variables are consistent again. If this is not done, future
+calls to pthread_mutex_lock() will continue to return EOWNERDEAD (although
+locking will function correctly).
+.SH RETURN VALUE
+.LP
+Upon successful completion, \fIpthread_mutexattr_setrobust_np\fP(),
+\fIpthread_mutexattr_getrobust_np\fP() and
+\fIpthread_mutex_consistent_np\fP()shall return zero; otherwise,
+an error number shall be returned to indicate the error.
+.SH ERRORS
+.LP
+The \fIpthread_mutexattr_destroy\fP() function may fail if:
+.TP 7
+.B EINVAL
+The value specified by \fIrobustness\fP is invalid.
+.LP
+The \fIpthread_mutex_consistent_np\fp() function may fail if:
+.TP 7
+.B EINVAL
+The mutex specified by \fImutex\fP is either not PTHREAD_MUTEX_ROBUST_NP or is
+in consistent state.
+\fIThe following sections are informative.\fP
+.SH EXAMPLES
+.LP
+The robost mutexes could be used to share a common lock accross multiple
+process and avoid IPC communication. Here is an example:
+.sp
+.RS
+.nf
+\fB
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define __USE_GNU
+#include <pthread.h>
+
+static const char *lock_name = "/dev/shm/limi_lock";
+static int lock_fd;
+static void *limi_ressource;
+static pthread_mutex_t *limi_mutex;
+
+.sp
+
+static int open_existing_lock(void)
+{
+ int fd;
+ int ret;
+ struct stat buf;
+ int retry = 5;
+
+ fd = open(lock_name, O_RDWR);
+ if (fd < 0)
+ return fd;
+ do {
+
+ ret = fstat(fd, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf.st_size == sizeof(*limi_mutex))
+ return fd;
+
+ sleep(1);
+ retry--;
+ } while (retry);
+
+ close(fd);
+ return -1;
+}
+
+.sp
+
+static int create_new_lock(void)
+{
+ int fd;
+ pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutexattr_t attr;
+ int ret;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
+ pthread_mutex_init(&cmutex, &attr);
+
+ fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP);
+ if (fd < 0)
+ return fd;
+
+ ret = write(fd, &cmutex, sizeof(cmutex));
+ if (ret < 0) {
+ fprintf(stderr, "Write to %s failed: %s\n",
+ lock_name, strerror(errno));
+ exit(1);
+ }
+ return fd;
+}
+
+.sp
+
+void limi_lock_init(void)
+{
+ lock_fd = open_existing_lock();
+ if (lock_fd < 0) {
+ lock_fd = create_new_lock();
+ if (lock_fd < 0) {
+ lock_fd = open_existing_lock();
+ if (lock_fd < 0) {
+ fprintf(stderr, "Can't open %s: %s\n",
+ lock_name, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ limi_lock_mmap = mmap(NULL, sizeof(*limi_mutex),
+ PROT_READ | PROT_WRITE, MAP_SHARED, lock_fd, 0);
+
+ if (limi_lock_mmap == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap limi lock: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ limi_mutex = limi_lock_mmap;
+}
+
+.sp
+
+void limi_lock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_lock(limi_mutex);
+ if (!ret)
+ return;
+
+ if (ret == EOWNERDEAD) {
+ pthread_mutex_consistent_np(limi_mutex);
+ return;
+ }
+
+ fprintf(stderr, "Can not grab lock: %s\n", strerror(ret));
+ exit(1);
+}
+
+.sp
+
+void limi_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(limi_mutex);
+ if (!ret)
+ return;
+
+ fprintf(stderr, "Can not unlock: %s\n", strerror(ret));
+ exit(1);
+}
+
+\fP
+.fi
+.RE
+.LP
+The code example shows how to share a lock between two applications without
+classic IPC. If one of the applications dies while holding the lock or the
+system reboots unexpectly, the new owner of lock marks the lock state
+consistent. In this example the lock owner does not need to perform any
+validation of the resource protected by the lock. The lock owner knows if
+the previous owner unlocked successfully or died.
+.f
+.SH SEE ALSO
+.LP
+\fIpthread_mutex_create\fP(), \fIpthread_mutex_destroy\fP(),
+\fIpthread_mutexattr_init\fP(), \fIpthread_mutexattr_destroy\fP(),
+\fIpthread_mutex_lock\fP(), \fIpthread_mutex_unlock\fP(), \fI<pthread.h>\fP
+.SH COPYRIGHT
+This man page was contributed by Sebastian Andrzej Siewior.


2008-12-07 16:45:13

by Michael Kerrisk

[permalink] [raw]
Subject: Re: man page for robust mutexes

Sebastian,

Thanks for writing this page!

I will have more comments later, but a few quick comments now. Could
you revise your page in the light of these comments?

On Mon, Dec 1, 2008 at 4:34 PM, Sebastian Andrzej Siewior
<[email protected]> wrote:
> Robust mutexes are available since a few days, so here is a man page for
> them. I used pthread_mutexattr_setprotocol.3p as a template and sneaked a
> few phrases from there.

Please be careful here. If there are any sentences taken from a
copyrighted source (the 3p pages are copyright by the IEEE), could you
please rewrite in your own words.

> That's my first man-page however, so some things
> might be wrong :)

Could you take a look at man-pages(7) please -- that will clarify some
of my comments below.

> Sebastian
> --- /dev/null
> +++ b/man3p/pthread_mutexattr_setrobust_np.3p
> @@ -0,0 +1,240 @@
> +.\" Copyright (c) 2008 Sebastian Andrzej Siewior

You need to put this page under a free license. Have a look here:
http://www.kernel.org/doc/man-pages/licenses.html
The "verbatim" license is the most widely used, and my preference for
new pages, but it's your choice.

> +.TH "ROBUST MUTEXES" P 2008 "" "POSIX Programmer's Manual"

s/P/3/

s/POSIX Programmer's Manual/Linux Programmer's Manual/

> +.\" pthread_mutexattr_setrobust_np
> +.SH NAME
> +pthread_mutexattr_setrobust_np, pthread_mutexattr_getrobust_np \- set or get
> +the robustness of a mutex
> +.SH SYNOPSIS
> +.LP

I prefer .nf/.fi blocks for the SYNOPIS, and then you can drop all of
the .br and .sp clutter. See, for example, fcntl(2).

> +\fB#include <pthread.h>

Please, rather make it .B for each of these lines, and .BI where you
need to mix with italic See, for example, fcntl(2).

> +.br
> +.sp
> +#define __USE_GNU

s/__USE_GNU/_GNU_SOURCE/

See feature_test_macros(7)

> +.sp
> +int pthread_mutexattr_getrobust_np(pthread_mutexattr_t *\fP\fIattr\fP\fB, int
> +\fP\fIrobustness\fP\fB);
> +.br
> +int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *\fP\fIattr\fP\fB, int
> +\fP\fIrobustness\fP\fB);
> +.br
> +int pthread_mutex_consistent_np(pthread_mutex_t *\fP\fImutex\fP\fB);
> +\fP
> +\fB
> +.br
> +\fP
> +.SH DESCRIPTION
> +.LP
> +The \fIpthread_mutexattr_getrobust_np\fP() and

The convention used in most man-pages sources is

.BR func ()

Could you please swich all instances to that.

> +\fIpthread_mutexattr_setrobust_np\fP() functions, respectively, shall get and
> +set the robustness attribute of a mutex attributes object pointed to by
> +\fIattr\fP which was previously created by the function

Preferred is:

.I attr

Could you please change all instances.

> +\fIpthread_mutexattr_init\fP().
> +.LP
> +The \fIrobustness\fP attribute defines the robustness to be used in utilizing
> +mutexes. The value of \fIrobustness\fP may be one of:

Please start new sentences on new source lines. (See man-pages(7).)

> +.LP
> +.sp
> +PTHREAD_MUTEX_STALLED_NP
> +.br
> +.sp
> +PTHREAD_MUTEX_ROBUST_NP
> +.br
> +.sp
> +.LP
> +which are defined in the \fI<pthread.h>\fP header if the GNU extensions are

Use ".I"

> +used.
> +.LP
> +The default attribute is PTHREAD_MUTEX_STALLED_NP.

Use .B for constants

> +.LP
> +When a mutex is created with PTHREAD_MUTEX_STALLED_NP is locked and the owner
> +dies, then the next call to pthread_mutex_lock() will block forever. Also,
> +the already waiting waiters will wait for ever.
> +.LP
> +The behavior is different if the mutex is created with
> +PTHREAD_MUTEX_ROBUST_NP. If the owner dies while holding the lock, the
> +next call to \fIpthread_mutex_lock\fP() will return EOWNERDEAD and the caller

Use .B for constants

> +will acquire lock. The new owner should call
> +\fIpthread_mutex_consistent_np\fP() on the mutex once the internal state of
> +the protected variables are consistent again. If this is not done, future
> +calls to pthread_mutex_lock() will continue to return EOWNERDEAD (although
> +locking will function correctly).
> +.SH RETURN VALUE
> +.LP

.LP is needed after .SH

> +Upon successful completion, \fIpthread_mutexattr_setrobust_np\fP(),
> +\fIpthread_mutexattr_getrobust_np\fP() and
> +\fIpthread_mutex_consistent_np\fP()shall return zero; otherwise,
> +an error number shall be returned to indicate the error.
> +.SH ERRORS
> +.LP
> +The \fIpthread_mutexattr_destroy\fP() function may fail if:

Wrong function nam above.

> +.TP 7
> +.B EINVAL
> +The value specified by \fIrobustness\fP is invalid.
> +.LP
> +The \fIpthread_mutex_consistent_np\fp() function may fail if:
> +.TP 7
> +.B EINVAL
> +The mutex specified by \fImutex\fP is either not PTHREAD_MUTEX_ROBUST_NP or is
> +in consistent state.

"in an incinsistent state"?

> +\fIThe following sections are informative.\fP
> +.SH EXAMPLES
> +.LP
> +The robost mutexes could be used to share a common lock accross multiple
> +process and avoid IPC communication. Here is an example:
> +.sp
> +.RS
> +.nf
> +\fB
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <sys/mman.h>
> +#include <errno.h>
> +
> +#define __USE_GNU

s/__USE_GNU/_GNU_SOURCE/
and move to top of listing (see feature_test_macros(7).

> +#include <pthread.h>
> +
> +static const char *lock_name = "/dev/shm/limi_lock";
> +static int lock_fd;
> +static void *limi_ressource;
> +static pthread_mutex_t *limi_mutex;
> +
> +.sp
> +
> +static int open_existing_lock(void)
> +{
> + int fd;
> + int ret;
> + struct stat buf;
> + int retry = 5;
> +
> + fd = open(lock_name, O_RDWR);
> + if (fd < 0)
> + return fd;
> + do {
> +
> + ret = fstat(fd, &buf);
> + if (ret < 0)
> + return ret;
> +
> + if (buf.st_size == sizeof(*limi_mutex))
> + return fd;
> +
> + sleep(1);
> + retry--;

s/-/\\-/ for each '-' in source code.

> + } while (retry);
> +
> + close(fd);
> + return -1;

See previous comment.

> +}
> +
> +.sp
> +
> +static int create_new_lock(void)
> +{
> + int fd;
> + pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
> + pthread_mutexattr_t attr;
> + int ret;
> +
> + pthread_mutexattr_init(&attr);
> + pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
> + pthread_mutex_init(&cmutex, &attr);
> +
> + fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
> + S_IRGRP | S_IWGRP);
> + if (fd < 0)
> + return fd;
> +
> + ret = write(fd, &cmutex, sizeof(cmutex));
> + if (ret < 0) {
> + fprintf(stderr, "Write to %s failed: %s\n",
> + lock_name, strerror(errno));
> + exit(1);
> + }
> + return fd;
> +}
> +
> +.sp
> +
> +void limi_lock_init(void)
> +{
> + lock_fd = open_existing_lock();
> + if (lock_fd < 0) {
> + lock_fd = create_new_lock();
> + if (lock_fd < 0) {
> + lock_fd = open_existing_lock();
> + if (lock_fd < 0) {
> + fprintf(stderr, "Can't open %s: %s\n",
> + lock_name, strerror(errno));
> + exit(1);
> + }
> + }
> + }
> +
> + limi_lock_mmap = mmap(NULL, sizeof(*limi_mutex),
> + PROT_READ | PROT_WRITE, MAP_SHARED, lock_fd, 0);
> +
> + if (limi_lock_mmap == MAP_FAILED) {
> + fprintf(stderr, "failed to mmap limi lock: %s\n",
> + strerror(errno));
> + exit(1);
> + }
> + limi_mutex = limi_lock_mmap;
> +}
> +
> +.sp
> +
> +void limi_lock(void)
> +{
> + int ret;
> +
> + ret = pthread_mutex_lock(limi_mutex);
> + if (!ret)
> + return;
> +
> + if (ret == EOWNERDEAD) {
> + pthread_mutex_consistent_np(limi_mutex);
> + return;
> + }
> +
> + fprintf(stderr, "Can not grab lock: %s\n", strerror(ret));
> + exit(1);
> +}
> +
> +.sp
> +
> +void limi_unlock(void)
> +{
> + int ret;
> +
> + ret = pthread_mutex_unlock(limi_mutex);
> + if (!ret)
> + return;
> +
> + fprintf(stderr, "Can not unlock: %s\n", strerror(ret));
> + exit(1);
> +}
> +
> +\fP
> +.fi
> +.RE
> +.LP

I think it's better to have the explanation of the programs before the
listing. Could you relocate it please.

> +The code example shows how to share a lock between two applications without
> +classic IPC. If one of the applications dies while holding the lock or the
> +system reboots unexpectly, the new owner of lock marks the lock state
> +consistent. In this example the lock owner does not need to perform any
> +validation of the resource protected by the lock. The lock owner knows if
> +the previous owner unlocked successfully or died.
> +.f
> +.SH SEE ALSO
> +.LP
> +\fIpthread_mutex_create\fP(), \fIpthread_mutex_destroy\fP(),
> +\fIpthread_mutexattr_init\fP(), \fIpthread_mutexattr_destroy\fP(),
> +\fIpthread_mutex_lock\fP(), \fIpthread_mutex_unlock\fP(), \fI<pthread.h>\fP
> +.SH COPYRIGHT
> +This man page was contributed by Sebastian Andrzej Siewior.

Take a look, and you'll see that *no* pages in man-pages carry
copyright notices in the formatted output. The copyright notice in
the page source suffices.


Cheers,

Michael

--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
git://git.kernel.org/pub/scm/docs/man-pages/man-pages.git
man-pages online: http://www.kernel.org/doc/man-pages/online_pages.html
Found a bug? http://www.kernel.org/doc/man-pages/reporting_bugs.html

Subject: [PATCH v2] add man-page for pthread_mutexattr_setrobust_np()

* Michael Kerrisk | 2008-12-07 11:44:54 [-0500]:

>Sebastian,
Michael,

>I will have more comments later, but a few quick comments now. Could
>you revise your page in the light of these comments?
Yup. Here is version two. I somehow mixed up the IEEE man-pages with
yours. So first I didn't understand most of you comments :) Once I
figured that out I tried to address all of your comments and I've also
rephrased everything. Be prepared :)

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
man3/pthread_mutex_consistent_np.3 | 1 +
man3/pthread_mutexattr_getrobust_np.3 | 1 +
man3/pthread_mutexattr_setrobust_np.3 | 255 +++++++++++++++++++++++++++++++++
3 files changed, 257 insertions(+), 0 deletions(-)
create mode 100644 man3/pthread_mutex_consistent_np.3
create mode 100644 man3/pthread_mutexattr_getrobust_np.3
create mode 100644 man3/pthread_mutexattr_setrobust_np.3

diff --git a/man3/pthread_mutex_consistent_np.3 b/man3/pthread_mutex_consistent_np.3
new file mode 100644
index 0000000..a92edb9
--- /dev/null
+++ b/man3/pthread_mutex_consistent_np.3
@@ -0,0 +1 @@
+.so man3/pthread_mutexattr_setrobust_np.3
diff --git a/man3/pthread_mutexattr_getrobust_np.3 b/man3/pthread_mutexattr_getrobust_np.3
new file mode 100644
index 0000000..a92edb9
--- /dev/null
+++ b/man3/pthread_mutexattr_getrobust_np.3
@@ -0,0 +1 @@
+.so man3/pthread_mutexattr_setrobust_np.3
diff --git a/man3/pthread_mutexattr_setrobust_np.3 b/man3/pthread_mutexattr_setrobust_np.3
new file mode 100644
index 0000000..34dbd6e
--- /dev/null
+++ b/man3/pthread_mutexattr_setrobust_np.3
@@ -0,0 +1,255 @@
+.\" Copyright (c) 2008 Sebastian Andrzej Siewior
+.\" Copyright (c) 2008 Linux Foundation, Michael Kerrisk
+.\" <[email protected]>
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+
+.TH "ROBUST MUTEXES" 3 2008-12-18 "GNU C Library" "Linux Programmer's Manual"
+.SH NAME
+pthread_mutexattr_setrobust_np, pthread_mutexattr_getrobust_np \- set or get
+the robustness of a mutex.
+.SH SYNOPSIS
+.nf
+.B #define _GNU_SOURCE
+.B #include <pthread.h>
+.sp
+.BI "int pthread_mutexattr_getrobust_np(pthread_mutexattr_t " *attr ",
+.BI " int " robustness ");"
+.BI "int pthread_mutexattr_setrobust_np(pthread_mutexattr_t " *attr ",
+.BI " int " robustness ");"
+.BI "int pthread_mutex_consistent_np(pthread_mutex_t " *mutex ");"
+.fi
+.sp
+Compile and link with \fI\-pthread\fP.
+.SH DESCRIPTION
+The
+.BR pthread_mutexattr_getrobust_np ()
+sets the robustness of the mutex attribute object referred to by
+.I attr
+to the value specified in
+.IR robustness .
+The
+.BR pthread_mutexattr_getrobust_np ()
+retrieves the current values of robustness of mutex attribute object referred
+to by
+.IR attr .
+The following values may be specified in
+.IR robustness :
+.TP
+.B PTHREAD_MUTEX_STALLED_NP
+Mutexes created using
+.I attr
+will stall if the owner dies while owning the lock.
+.PP
+The default setting of the robustness attribute in a newly initialized mutex
+object is
+.BR PTHREAD_MUTEX_STALLED_NP .
+.TP
+.B PTHREAD_MUTEX_ROBUST_NP
+Mutexes created using
+.I attr
+will be reboots. If the owner dies while holding the lock
+.BR pthread_mutex_lock ()
+will return
+.BR EOWNERDEAD
+.B and
+the caller is holding lock.
+This return value shall signalize the caller that the resource which is
+protected by the mutex may be in an inconsistent state and should be verified
+before used.
+Once the lock holder sanitized the protected resource he should invoke
+.BR pthread_mutex_consistent_np ()
+on the mutex object to mark that mutex as consistent.
+If this is not done, future locking requests will return
+.B EOWNERDEAD
+although the previous owner did not die.
+.SH "RETURN VALUE"
+On success, these functions return 0;
+on error, they return a non-zero error number.
+.SH ERRORS
+.BR pthread_mutexattr_setrobust_np ()
+can fail with the following error:
+.TP
+.B EINVAL
+Invalid value in
+.IR robustness .
+.PP
+.BR pthread_mutexattr_getrobust_np ()
+can fail with the following error:
+.TP
+.B EINVAL
+Invalid value in
+.IR robustness .
+.PP
+.BR pthread_mutex_consistent_np ()
+can fail with the following error:
+.TP
+.B EINVAL
+The mutex object specified by
+.I mutex
+is either not
+.B PTHREAD_MUTEX_ROBUST_NP
+or is in a consistent state.
+.PP
+.SH EXAMPLE
+The code example shows how to share a lock between two applications without
+System V IPC.
+An advantage over System V semaphores is that the kernel is not invoked in
+case the lock is not hold.
+If one of the applications dies while holding the lock or the system reboots
+unexpectedly, the new owner of lock marks the lock state consistent.
+In this example the lock owner does not need to perform any validation of the
+resource protected by the lock.
+
+.nf
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static const char *lock_name = "/dev/shm/limi_lock";
+static pthread_mutex_t *limi_mutex;
+
+static int open_existing_lock(void)
+{
+ int fd;
+ int ret;
+ struct stat buf;
+ int retry = 5;
+
+ fd = open(lock_name, O_RDWR);
+ if (fd < 0)
+ return fd;
+ do {
+ ret = fstat(fd, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf.st_size == sizeof(*limi_mutex))
+ return fd;
+
+ close(fd);
+ sleep(1);
+ retry\-\-;
+ } while (retry);
+
+ close(fd);
+ return \-1;
+}
+
+static int create_new_lock(void)
+{
+ int fd;
+ pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutexattr_t attr;
+ int ret;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
+ pthread_mutex_init(&cmutex, &attr);
+
+ fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP);
+ if (fd < 0)
+ return fd;
+
+ ret = write(fd, &cmutex, sizeof(cmutex));
+ if (ret < 0) {
+ fprintf(stderr, "Write to %s failed: %s\\n",
+ lock_name, strerror(errno));
+ exit(1);
+ }
+ return fd;
+}
+
+void limi_lock_init(void)
+{
+ void *limi_lock_mmap;
+ int lock_fd;
+
+ lock_fd = open_existing_lock();
+ if (lock_fd < 0) {
+ lock_fd = create_new_lock();
+ if (lock_fd < 0) {
+ lock_fd = open_existing_lock();
+ if (lock_fd < 0) {
+ fprintf(stderr, "Can't open %s: %s\\n",
+ lock_name, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ limi_lock_mmap = mmap(NULL, sizeof(*limi_mutex),
+ PROT_READ | PROT_WRITE, MAP_SHARED, lock_fd, 0);
+ close(lock_fd);
+ if (limi_lock_mmap == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap limi lock: %s\\n",
+ strerror(errno));
+ exit(1);
+ }
+ limi_mutex = limi_lock_mmap;
+}
+
+void limi_lock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_lock(limi_mutex);
+ if (!ret)
+ return;
+
+ if (ret == EOWNERDEAD) {
+ pthread_mutex_consistent_np(limi_mutex);
+ return;
+ }
+
+ fprintf(stderr, "Can not grab lock: %s\\n", strerror(ret));
+ exit(1);
+}
+
+void limi_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(limi_mutex);
+ if (!ret)
+ return;
+
+ fprintf(stderr, "Can not unlock: %s\\n", strerror(ret));
+ exit(1);
+}
+
+.fi
+.SH "SEE ALSO"
+.BR pthread_mutex_create (3),
+.BR pthread_mutexattr_init (3),
+.BR pthread_mutex_lock (3),
+.BR pthread_mutex_unlock (3)
--
1.6.0.4

2008-12-22 07:32:26

by Bert Wesarg

[permalink] [raw]
Subject: Re: [PATCH v2] add man-page for pthread_mutexattr_setrobust_np()

On Sun, Dec 21, 2008 at 21:59, Sebastian Andrzej Siewior
<[email protected]> wrote:
> * Michael Kerrisk | 2008-12-07 11:44:54 [-0500]:
>
>>Sebastian,
> Michael,
Sebastian, Michael,

> +.SH EXAMPLE
> +The code example shows how to share a lock between two applications without
> +System V IPC.
> +An advantage over System V semaphores is that the kernel is not invoked in
> +case the lock is not hold.
> +If one of the applications dies while holding the lock or the system reboots
> +unexpectedly, the new owner of lock marks the lock state consistent.
> +In this example the lock owner does not need to perform any validation of the
> +resource protected by the lock.
> +
> +.nf
> +#define _GNU_SOURCE
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +
> +static const char *lock_name = "/dev/shm/limi_lock";
> +static pthread_mutex_t *limi_mutex;
> +
> +static int open_existing_lock(void)
> +{
> + int fd;
> + int ret;
> + struct stat buf;
> + int retry = 5;
> +
> + fd = open(lock_name, O_RDWR);
> + if (fd < 0)
> + return fd;
> + do {
> + ret = fstat(fd, &buf);
> + if (ret < 0)
Isn't here a close(2) missing?

> + return ret;
> +
> + if (buf.st_size == sizeof(*limi_mutex))
> + return fd;
> +
> + close(fd);
Isn't this close(2) wrong here?

> + sleep(1);
> + retry\-\-;
> + } while (retry);
> +
> + close(fd);
> + return \-1;
> +}
> +
> +static int create_new_lock(void)
> +{
> + int fd;
> + pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
> + pthread_mutexattr_t attr;
> + int ret;
> +
> + pthread_mutexattr_init(&attr);
> + pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
> + pthread_mutex_init(&cmutex, &attr);
> +
> + fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
> + S_IRGRP | S_IWGRP);
> + if (fd < 0)
> + return fd;
> +
> + ret = write(fd, &cmutex, sizeof(cmutex));
I think its undefined behavior if you copy a struct pthread_mutex. You
should use mmap here too.

> + if (ret < 0) {
> + fprintf(stderr, "Write to %s failed: %s\\n",
> + lock_name, strerror(errno));
> + exit(1);
> + }
> + return fd;
> +}

Subject: Re: [PATCH v2] add man-page for pthread_mutexattr_setrobust_np()

* Bert Wesarg | 2008-12-22 08:32:15 [+0100]:

>> +static int open_existing_lock(void)
>> +{
>> + int fd;
>> + int ret;
>> + struct stat buf;
>> + int retry = 5;
>> +
>> + fd = open(lock_name, O_RDWR);
>> + if (fd < 0)
>> + return fd;
>> + do {
>> + ret = fstat(fd, &buf);
>> + if (ret < 0)
>Isn't here a close(2) missing?
yes it is. Thx.

>> + return ret;
>> +
>> + if (buf.st_size == sizeof(*limi_mutex))
>> + return fd;
>> +
>> + close(fd);
>Isn't this close(2) wrong here?
uhhh. Actually I wanted to have the open() within the do while loop. So
I move that and this will be fine then.

>> + sleep(1);
>> + retry\-\-;
>> + } while (retry);
>> +
>> + close(fd);
>> + return \-1;
>> +}
>> +
>> +static int create_new_lock(void)
>> +{
>> + int fd;
>> + pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
>> + pthread_mutexattr_t attr;
>> + int ret;
>> +
>> + pthread_mutexattr_init(&attr);
>> + pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
>> + pthread_mutex_init(&cmutex, &attr);
>> +
>> + fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
>> + S_IRGRP | S_IWGRP);
>> + if (fd < 0)
>> + return fd;
>> +
>> + ret = write(fd, &cmutex, sizeof(cmutex));
>I think its undefined behavior if you copy a struct pthread_mutex. You
>should use mmap here too.
Why should be this undefined? Is there something special about this
struct? And why should this behave different with mmap() ?

Sebastian

2008-12-22 11:05:43

by Bert Wesarg

[permalink] [raw]
Subject: Re: [PATCH v2] add man-page for pthread_mutexattr_setrobust_np()

>>> +static int create_new_lock(void)
>>> +{
>>> + int fd;
>>> + pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
>>> + pthread_mutexattr_t attr;
>>> + int ret;
>>> +
>>> + pthread_mutexattr_init(&attr);
>>> + pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
>>> + pthread_mutex_init(&cmutex, &attr);
>>> +
>>> + fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR |
>>> + S_IRGRP | S_IWGRP);
>>> + if (fd < 0)
>>> + return fd;
>>> +
>>> + ret = write(fd, &cmutex, sizeof(cmutex));
>>I think its undefined behavior if you copy a struct pthread_mutex. You
>>should use mmap here too.
> Why should be this undefined? Is there something special about this
> struct? And why should this behave different with mmap() ?
If you would use mmap, you would initialize the mutex inside the
mmaped area, i.e. directly in the file.

To the copying:

Short answer: http://www.lambdacs.com/cpt/FAQ.html#Q15

Slightly longer:

pthread_mutex_t m1, m2;
pthread_mutex_init(&m1, NULL);
pthread_mutex_lock(&m1);
m2 = m1;
pthread_mutex_unlock(&m2);

How can you be sure, that you have unlocked m1 here?

Yes, you throw away the cmutex after returning from the function and
the copy inside the file is the only one left. I still think such code
should not be in a documentation.

Bert
>
> Sebastian
>