Subject: Re: Threading problems
To: Jaromir Dolecek <jdolecek@NetBSD.org>
From: Nathan J. Williams <nathanw@wasabisystems.com>
List: tech-userlevel
Date: 12/06/2004 14:13:03
"Nathan J. Williams" <nathanw@wasabisystems.com> writes:

> Thinking about this more, a better answer might be for <pthread.h> to
> #define pthread_mutex_lock(x) to __libc_mutex_lock(x), so that ~all
> code that is written to use locking routines from pthread.h will go
> through the libc redirection by default, instead of making them
> rewrite to mutex_lock() [that those names are used in libc is
> something of a historical accident involving the import of Sun code
> before pthreads were firmly entrenched as the only real thread
> interface].

Okay, here's my proposed patch to implement this scheme. It seems to
work on my systems, and I was able to rebuild a bunch of pkgsrc with
no ill effects on the programs. I was able to remove the libpthread
dependancy from, for example pkgsrc/graphics/gd, which is a prime
example of "defensive-only" threading.

Following this, I'll fold threadlib.h back into an intewrnal libc
heade, and switch xsrc back to using pthread.h and standard functions
for its threading. As far as I know, libc and xsrc's Xthreads.h are
the only consumers of threadlib.h.

        - Nathan

Index: lib/libpthread/pthread.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread.h,v
retrieving revision 1.16
diff -u -r1.16 pthread.h
--- lib/libpthread/pthread.h	7 Dec 2003 20:29:07 -0000	1.16
+++ lib/libpthread/pthread.h	6 Dec 2004 19:00:27 -0000
@@ -220,4 +220,113 @@
 #define PTHREAD_RWLOCK_INITIALIZER	_PTHREAD_RWLOCK_INITIALIZER
 #define PTHREAD_SPINLOCK_INITIALIZER	_PTHREAD_SPINLOCK_INITIALIZER
 
+/*
+ * Use macros to rename many pthread functions to the corresponding
+ * libc symbols which are either trivial/no-op stubs or the real
+ * thing, depending on whether libpthread is linked in to the
+ * program. This permits code, particularly libraries that do not
+ * directly use threads but want to be thread-safe in the presence of
+ * threaded callers, to use pthread mutexes and the like without
+ * unnecessairly including libpthread in their linkage.
+ *
+ * Left out of this list are functions that can't sensibly be trivial
+ * or no-op stubs in a single-threaded process (pthread_create,
+ * pthread_kill, pthread_detach), functions that normally block and
+ * wait for another thread to do something (pthread_join,
+ * pthread_cond_wait, pthread_cond_timedwait), and functions that
+ * don't make sense without the previous functions (pthread_attr_*).
+ *
+ * The rename is done as:
+ * #define pthread_foo	__libc_foo
+ * instead of
+ * #define pthread_foo(x) __libc_foo((x))
+ * in order that taking the address of the function ("func =
+ * &pthread_foo;") continue to work.
+ *
+ * POSIX/SUSv3 requires that its functions exist as functions (even if
+ * macro versions exist) and specifically that "#undef pthread_foo" is
+ * legal and should not break anything. Code that does such will not
+ * successfully get the stub behavior implemented here and will
+ * require libpthread to be linked in.
+ */
+
+#ifndef __LIBPTHREAD_SOURCE__
+__BEGIN_DECLS
+int	__libc_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
+int	__libc_mutex_lock(pthread_mutex_t *);
+int	__libc_mutex_trylock(pthread_mutex_t *);
+int	__libc_mutex_unlock(pthread_mutex_t *);
+int	__libc_mutex_destroy(pthread_mutex_t *);
+
+int	__libc_mutexattr_init(pthread_mutexattr_t *);
+int	__libc_mutexattr_settype(pthread_mutexattr_t *, int);
+int	__libc_mutexattr_destroy(pthread_mutexattr_t *);
+__END_DECLS
+
+#define	pthread_mutex_init		__libc_mutex_init
+#define	pthread_mutex_lock		__libc_mutex_lock
+#define	pthread_mutex_trylock		__libc_mutex_trylock
+#define	pthread_mutex_unlock		__libc_mutex_unlock
+#define	pthread_mutex_destroy		__libc_mutex_destroy
+
+#define	pthread_mutexattr_init		__libc_mutexattr_init
+#define	pthread_mutexattr_settype	__libc_mutexattr_settype
+#define	pthread_mutexattr_destroy	__libc_mutexattr_destroy
+
+__BEGIN_DECLS
+int	__libc_cond_init(pthread_cond_t *, const pthread_condattr_t *);
+int	__libc_cond_signal(pthread_cond_t *);
+int	__libc_cond_broadcast(pthread_cond_t *);
+int	__libc_cond_destroy(pthread_cond_t *);
+__END_DECLS
+
+#define	pthread_cond_init	     	__libc_cond_init
+#define	pthread_cond_signal		__libc_cond_signal
+#define	pthread_cond_broadcast		__libc_cond_broadcast
+#define	pthread_cond_destroy		__libc_cond_destroy
+
+__BEGIN_DECLS
+int	__libc_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *);
+int	__libc_rwlock_rdlock(pthread_rwlock_t *);
+int	__libc_rwlock_wrlock(pthread_rwlock_t *);
+int	__libc_rwlock_tryrdlock(pthread_rwlock_t *);
+int	__libc_rwlock_trywrlock(pthread_rwlock_t *);
+int	__libc_rwlock_unlock(pthread_rwlock_t *);
+int	__libc_rwlock_destroy(pthread_rwlock_t *);
+__END_DECLS
+
+#define	pthread_rwlock_init		__libc_rwlock_init
+#define	pthread_rwlock_rdlock		__libc_rwlock_rdlock
+#define	pthread_rwlock_wrlock		__libc_rwlock_wrlock
+#define	pthread_rwlock_tryrdlock	__libc_rwlock_tryrdlock
+#define	pthread_rwlock_trywrlock	__libc_rwlock_trywrlock
+#define	pthread_rwlock_unlock		__libc_rwlock_unlock
+#define	pthread_rwlock_destroy		__libc_rwlock_destroy
+
+__BEGIN_DECLS
+int	__libc_thr_keycreate(pthread_key_t *, void (*)(void *));
+int	__libc_thr_setspecific(pthread_key_t, const void *);
+void	*__libc_thr_getspecific(pthread_key_t);
+int	__libc_thr_keydelete(pthread_key_t);
+__END_DECLS
+
+#define	pthread_key_create		__libc_thr_keycreate
+#define	pthread_setspecific		__libc_thr_setspecific
+#define	pthread_getspecific		__libc_thr_getspecific
+#define	pthread_key_delete		__libc_thr_keydelete
+
+__BEGIN_DECLS
+int	__libc_thr_once(pthread_once_t *, void (*)(void));
+pthread_t	__libc_thr_self(void);
+void	__libc_thr_exit(void *) __attribute__((__noreturn__));
+int	__libc_thr_setcancelstate(int, int *);
+__END_DECLS
+
+#define	pthread_once			__libc_thr_once
+#define	pthread_self			__libc_thr_self
+#define	pthread_exit			__libc_thr_exit
+#define	pthread_setcancelstate		__libc_thr_setcancelstate
+
+#endif /* __LIBPTHREAD_SOURCE__ */
+
 #endif /* _LIB_PTHREAD_H */
Index: lib/libpthread/Makefile
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Makefile,v
retrieving revision 1.27
diff -u -r1.27 Makefile
--- lib/libpthread/Makefile	2 Jun 2004 14:07:07 -0000	1.27
+++ lib/libpthread/Makefile	6 Dec 2004 19:00:27 -0000
@@ -26,6 +26,7 @@
 .PATH:	${ARCHDIR}
 
 CPPFLAGS+=	-I${ARCHDIR} -I${.CURDIR} -I${.OBJDIR} -D_LIBC
+CPPFLAGS+=	-D__LIBPTHREAD_SOURCE__
 
 DPSRCS+=	assym.h
 CLEANFILES+=	assym.h
Index: include/sched.h
===================================================================
RCS file: /cvsroot/src/include/sched.h,v
retrieving revision 1.4
diff -u -r1.4 sched.h
--- include/sched.h	8 Jul 2003 05:41:51 -0000	1.4
+++ include/sched.h	6 Dec 2004 19:00:27 -0000
@@ -64,8 +64,13 @@
 
 /* Not optional in the presence of _POSIX_THREADS */
 int	sched_yield(void);
+void	__libc_thr_yield(void);
 __END_DECLS
 
+#ifndef __LIBPTHREAD_SOURCE__
+#define sched_yield		__libc_thr_yield
+#endif /* __LIBPTHREAD_SOURCE__ */
+
 #if defined(_NETBSD_SOURCE)
 
 /*
Index: include/signal.h
===================================================================
RCS file: /cvsroot/src/include/signal.h,v
retrieving revision 1.38
diff -u -r1.38 signal.h
--- include/signal.h	1 Jul 2004 23:46:07 -0000	1.38
+++ include/signal.h	6 Dec 2004 19:00:28 -0000
@@ -66,6 +66,10 @@
     defined(_NETBSD_SOURCE)
 int	pthread_sigmask __P((int, const sigset_t *, sigset_t *));
 int	pthread_kill __P((pthread_t, int));
+int	__libc_thr_sigsetmask(int, const sigset_t *, sigset_t *);
+#ifndef __LIBPTHREAD_SOURCE__
+#define	pthread_sigmask		__libc_thr_sigsetmask
+#endif /* __LIBPTHREAD_SOURCE__ */
 #endif
 
 #ifdef __LIBC12_SOURCE__