Skip to contentSkip to conversation

lib: add safe_pthread_create() & safe_pthread_join()

PATCH New Alexey Kodanev 2 participants 8 messages Apr 7, 2016 – Apr 12, 2016 Library code

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/2 lib: add safe_pthread_create() & safe_pthread_join()

---
 include/tst_safe_pthread.h |   32 ++++++++++++++++++++++++++
 include/tst_test.h         |    1 +
 lib/safe_pthread.c         |   54 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 0 deletions(-)
 create mode 100644 include/tst_safe_pthread.h
 create mode 100644 lib/safe_pthread.c
include/tst_safe_pthread.h new file +32 -0
new file mode 100644
index 0000000..810ddcb
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TST_SAFE_PTHREAD_H__
+#define TST_SAFE_PTHREAD_H__
+
+int safe_pthread_create(const char *file, const int lineno,
+			pthread_t *thread_id, const pthread_attr_t *attr,
+			void *(*thread_fn)(void *), void *arg);
+#define SAFE_PTHREAD_CREATE(thread_id, attr, thread_fn, arg) \
+	safe_pthread_create(__FILE__, __LINE__, thread_id, attr, thread_fn, arg)
+
+int safe_pthread_join(const char *file, const int lineno,
+		      pthread_t thread_id, void **retval);
+#define SAFE_PTHREAD_JOIN(thread_id, retval) \
+	safe_pthread_join(__FILE__, __LINE__, thread_id, retval)
+
+#endif /* TST_SAFE_PTHREAD_H__ */
include/tst_test.h +1 -0
index 2c35dbd..88b46d6 100644
@@ -60,6 +60,7 @@ pid_t safe_fork(const char *filename, unsigned int lineno);
 #include "tst_safe_macros.h"
 #include "tst_safe_file_ops.h"
 #include "tst_safe_net.h"
+#include "tst_safe_pthread.h"
 
 struct tst_option {
 	char *optstr;
lib/safe_pthread.c new file +54 -0

54 lines not on this page · raw message

Signature · 1 line
1.7.1
#1 lore raw

PATCH v2 2/2 syscalls/fcntl: add new test for open file description locks

OFD lock is available since Linux 3.15 and new F_OFD_* macros added to lapi/fcntl.h

Test is based on [1] article and checks that OFD locks can syncronise access to a file between multiple threads.

[1] https://lwn.net/Articles/586904

---
v2: * correct varibale names
* use new library API
* truncate test file before threads start
* use safe_pthread_*()

 include/lapi/fcntl.h                      |   12 +++
 runtest/syscalls                          |    2 +
 testcases/kernel/syscalls/.gitignore      |    2 +
 testcases/kernel/syscalls/fcntl/Makefile  |    3 +
 testcases/kernel/syscalls/fcntl/fcntl34.c |  149 +++++++++++++++++++++++++++++
 5 files changed, 168 insertions(+), 0 deletions(-)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl34.c
include/lapi/fcntl.h +12 -0
index 1ea3fb4..c4d4fb5 100644
@@ -43,6 +43,18 @@
 # define F_OWNER_PGRP 2
 #endif
 
+#ifndef F_OFD_GETLK
+# define F_OFD_GETLK	36
+#endif
+
+#ifndef F_OFD_SETLK
+# define F_OFD_SETLK	37
+#endif
+
+#ifndef F_OFD_SETLKW
+# define F_OFD_SETLKW	38
+#endif
+
 #ifndef AT_FDCWD
 # define AT_FDCWD -100
 #endif
runtest/syscalls +2 -0
index f537374..ca922e6 100644
@@ -265,6 +265,8 @@ fcntl32 fcntl32
 fcntl32_64 fcntl32_64
 fcntl33 fcntl33
 fcntl33_64 fcntl33_64
+fcntl34 fcntl34
+fcntl34_64 fcntl34_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
testcases/kernel/syscalls/.gitignore +2 -0
index e2fa551..ad2618c 100644
@@ -228,6 +228,8 @@
 /fcntl/fcntl32_64
 /fcntl/fcntl33
 /fcntl/fcntl33_64
+/fcntl/fcntl34
+/fcntl/fcntl34_64
 /fdatasync/fdatasync01
 /fdatasync/fdatasync02
 /flock/flock01
testcases/kernel/syscalls/fcntl/Makefile +3 -0
index 214eeb8..d78dd72 100644
@@ -21,6 +21,9 @@ top_srcdir		?= ../../../..
 fcntl33: LDLIBS+=-lrt
 fcntl33_64: LDLIBS+=-lrt
 
+fcntl34: LDLIBS += -lpthread
+fcntl34_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
testcases/kernel/syscalls/fcntl/fcntl34.c new file +149 -0

149 lines not on this page · raw message

Signature · 1 line
1.7.1
#2 lore raw

PATCH v2 2/2 syscalls/fcntl: add new test for open file description locks

Hi!

Quoted 75 lines
testcases/kernel/syscalls/fcntl/fcntl34.c new file +75 -0
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "lapi/fcntl.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_locks";
+const int writes_num = 100;
+const int write_size = 4096;
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+static void spawn_threads(pthread_t *id, void *(*thread_fn)(void *))
+{
+	intptr_t i;
+
+	tst_res(TINFO, "spawning '%d' threads", thread_cnt);
+	for (i = 0; i < thread_cnt; ++i)
+		SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, (void *)i);
+}
+
+static void wait_threads(pthread_t *id)
+{
+	int i;
+
+	tst_res(TINFO, "waiting for '%d' threads", thread_cnt);
+	for (i = 0; i < thread_cnt; ++i)
+		SAFE_PTHREAD_JOIN(id[i], NULL);
+}
+
+void *thread_fn_01(void *arg)
+{
+	int i;
+	char buf[write_size];
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	memset(buf, (intptr_t)arg, write_size);
+
+	struct flock lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = 0,
+		.l_len    = 1,
+	};
+
+	for (i = 0; i < writes_num; ++i) {
+		lck.l_type = F_WRLCK;
+		if (fcntl(fd, F_OFD_SETLKW, &lck) == -1)
+			tst_brk(TBROK | TERRNO, "fcntl() failed");
+
+		SAFE_LSEEK(fd, 0, SEEK_END);
+		SAFE_WRITE(1, fd, buf, write_size);
+
+		lck.l_type = F_UNLCK;
+		if (fcntl(fd, F_OFD_SETLKW, &lck) == -1)
+			tst_brk(TBROK | TERRNO, "fcntl() failed");
+
+		pthread_yield();
+	}
+
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+static void test01(void)

testcases/kernel/syscalls/fcntl/fcntl34.c line 105

Quoted 10 lines
testcases/kernel/syscalls/fcntl/fcntl34.c new file +10 -0
+{
+	intptr_t i;
+	int k;
+	pthread_t id[thread_cnt];
+	int res[thread_cnt];
+	char buf[write_size];
+
+	tst_res(TINFO, "write to a file inside threads with OFD locks");
+
+	int fd = SAFE_OPEN(fname, O_CREAT | O_TRUNC | O_RDONLY, 0600);

I'm not sure that O_TRUNC | O_RDONLY would work. The manual says that the result is unspecified.

We should either change the O_RDONLY for O_RDWR or simply unlink() the file before we open it.

Otherwise (with the unsigned char buffer) it looks fine.

Signature · 2 lines
Cyril Hrubis
[email protected]
#3 lore raw

PATCH v2 2/2 syscalls/fcntl: add new test for open file description locks

In reply to Alexey Kodanev

Hi,

Quoted 7 lines · Alexey Kodanev

OFD lock is available since Linux 3.15 and new F_OFD_* macros added to lapi/fcntl.h

Test is based on [1] article and checks that OFD locks can syncronise access to a file between multiple threads.

[1] https://lwn.net/Articles/586904

Patch-set applied.

Best regards,
Alexey

#4 lore raw

PATCH v2 1/2 lib: add safe_pthread_create() & safe_pthread_join()

In reply to Alexey Kodanev

Hi!

lib/safe_pthread.c line 50

Quoted 11 lines
lib/safe_pthread.c new file +11 -0
+int safe_pthread_join(const char *file, const int lineno,
+		      pthread_t thread_id, void **retval)
+{
+	int rval;
+
+	rval = pthread_join(thread_id, retval);
+
+	if (rval) {
+		tst_brk_(file, lineno, TBROK,
+			 "pthread_join(%lu,%p) failed: %s", thread_id, retval,
+			 tst_strerrno(rval));

Technically the thread_id does not need to be numeric type, POSIX defines it as a opaque, may be structure as well.

But on the other hand I doubt that there is a Linux libc implementation that actually defines it to be anything else than long int.

lib/safe_pthread.c line 38

Quoted 4 lines
lib/safe_pthread.c new file +4 -0
+	}
+
+	return rval;
+}

Otherwise this looks fine.

Signature · 2 lines
Cyril Hrubis
[email protected]
#5 lore raw

PATCH v2 1/2 lib: add safe_pthread_create() & safe_pthread_join()

Hi,

Quoted 14 lines · Cyril Hrubis

Hi!

lib/safe_pthread.c line 50

Quoted 11 lines
lib/safe_pthread.c new file +11 -0
+int safe_pthread_join(const char *file, const int lineno,
+		      pthread_t thread_id, void **retval)
+{
+	int rval;
+
+	rval = pthread_join(thread_id, retval);
+
+	if (rval) {
+		tst_brk_(file, lineno, TBROK,
+			 "pthread_join(%lu,%p) failed: %s", thread_id, retval,
+			 tst_strerrno(rval));

Technically the thread_id does not need to be numeric type, POSIX defines it as a opaque, may be structure as well.

We could print in hex like this:

if (rval) {
     unsigned int i;
     unsigned char *ptr = (unsigned char *)&thread_id;
     size_t tid_size = sizeof(thread_id);
     char buf[tid_size * 2 + 1];

     for (i = 0; i < tid_size; ++i)
         sprintf(buf + i * 2, "%02x", ptr[i]);

     tst_brk_(file, lineno, TBROK,
              "pthread_join(%s,%p) failed: %s", buf, retval, ...);
}

But bytes might be in different order due to endianness, supposing we compare this value with "%lx", e.g. on x86_64

00971fe3257f0000 vs 7f25e31f9700 (%lx)

What do you think?

Thanks,
Alexey

Quoted 9 lines

But on the other hand I doubt that there is a Linux libc implementation that actually defines it to be anything else than long int.

lib/safe_pthread.c line 38

Quoted 4 lines
lib/safe_pthread.c new file +4 -0
+	}
+
+	return rval;
+}

Otherwise this looks fine.

#6 lore raw

PATCH v2 1/2 lib: add safe_pthread_create() & safe_pthread_join()

Hi!

Quoted 18 lines
Quoted 2 lines

Technically the thread_id does not need to be numeric type, POSIX defines it as a opaque, may be structure as well.

We could print in hex like this:

if (rval) {
     unsigned int i;
     unsigned char *ptr = (unsigned char *)&thread_id;
     size_t tid_size = sizeof(thread_id);
     char buf[tid_size * 2 + 1];

     for (i = 0; i < tid_size; ++i)
         sprintf(buf + i * 2, "%02x", ptr[i]);

     tst_brk_(file, lineno, TBROK,
              "pthread_join(%s,%p) failed: %s", buf, retval, ...);
}
Quoted 6 lines

But bytes might be in different order due to endianness, supposing we compare this value with "%lx", e.g. on x86_64

00971fe3257f0000 vs 7f25e31f9700 (%lx)

What do you think?

I'm starting to consider not printing the value at all, since it's not that important as gdb and strace pthread ids are not related to the value of pthread_t. We can just print "pthread_join(..., %p)" instead.

Or we can add a configure check if pthread_t is actually numeric. Something as:

	pthread_t *t = (unsigned long*)NULL;

Should fail with -Werror unless pthread_t is defined as unsigned long.

Then we can proceed with printing it with "%ul" if the check was OK, but I'm not sure that it's worth of the work.

Signature · 2 lines
Cyril Hrubis
[email protected]
#7 lore raw

PATCH v2 1/2 lib: add safe_pthread_create() & safe_pthread_join()

Hi,

Quoted 21 lines · Cyril Hrubis

Hi!

Quoted 20 lines
Quoted 2 lines

Technically the thread_id does not need to be numeric type, POSIX defines it as a opaque, may be structure as well.

We could print in hex like this:

if (rval) {
      unsigned int i;
      unsigned char *ptr = (unsigned char *)&thread_id;
      size_t tid_size = sizeof(thread_id);
      char buf[tid_size * 2 + 1];

      for (i = 0; i < tid_size; ++i)
          sprintf(buf + i * 2, "%02x", ptr[i]);

      tst_brk_(file, lineno, TBROK,
               "pthread_join(%s,%p) failed: %s", buf, retval, ...);
}

But bytes might be in different order due to endianness, supposing we compare this value with "%lx", e.g. on x86_64

Quoted 6 lines
Quoted 3 lines

00971fe3257f0000 vs 7f25e31f9700 (%lx)

What do you think?

I'm starting to consider not printing the value at all, since it's not that important as gdb and strace pthread ids are not related to the value of pthread_t. We can just print "pthread_join(..., %p)" instead.

Agree.

Quoted 9 lines

Or we can add a configure check if pthread_t is actually numeric. Something as:

	pthread_t *t = (unsigned long*)NULL;

Should fail with -Werror unless pthread_t is defined as unsigned long.

Then we can proceed with printing it with "%ul" if the check was OK, but I'm not sure that it's worth of the work.

OK. For now, we can add it into TODO list.

Thanks,
Alexey