diff options
Diffstat (limited to 'libpthread')
109 files changed, 15617 insertions, 29 deletions
diff --git a/libpthread/Makefile b/libpthread/Makefile index 66414ecbc..4dad83985 100644 --- a/libpthread/Makefile +++ b/libpthread/Makefile @@ -1,7 +1,6 @@ -# Makefile for uClibc +# Makefile for uClibc's pthread library  # -# Copyright (C) 2000 by by Lineo, inc. and Erik Andersen -# Copyright (C) 2000, 2001 by Erik Andersen <andersee@debian.org> +# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org>  #  # This program is free software; you can redistribute it and/or modify it under  # the terms of the GNU Library General Public License as published by the Free @@ -16,30 +15,49 @@  # You should have received a copy of the GNU Library General Public License  # along with this program; if not, write to the Free Software Foundation, Inc.,  # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Derived in part from the Linux-8086 C library, the GNU C Library, and several -# other sundry sources.  Files within this library are copyright by their -# respective copyright holders.  TOPDIR=../  include $(TOPDIR)Rules.mak +#Adjust the soname version to avoid namespace collisions with glibc's libpthread +PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION) +  LIBPTHREAD=libpthread.a  LIBPTHREAD_SHARED=libpthread.so -LIBPTHREAD_SHARED_FULLNAME=libpthread-$(MAJOR_VERSION).$(MINOR_VERSION).so +LIBPTHREAD_SHARED_FULLNAME=libpthread-$(PT_VERSION).so + +LIBTHREAD_DB=libthread_db.a +LIBTHREAD_DB_SHARED=libthread_db.so +LIBTHREAD_DB_SHARED_FULLNAME=libthread_db-$(PT_VERSION).so -CSRC = pthread.c -OBJS=$(patsubst %.c,%.o, $(CSRC)) +DIRS= +ifeq ($(strip $(INCLUDE_THREADS)),true) +	DIRS+=linuxthreads +ifeq ($(strip $(DODEBUG)),true) +	DIRS+=linuxthreads_db +endif +endif -all: $(OBJS) $(LIBPTHREAD) +ALL_SUBDIRS = linuxthreads linuxthreads_db -$(LIBPTHREAD): ar-target +all: $(LIBPTHREAD) $(LIBTHREAD_DB) + +$(LIBPTHREAD): subdirs +	@if [ -f $(LIBPTHREAD) ] ; then \ +		set -e; \ +		install -d $(TOPDIR)lib; \ +		rm -f $(TOPDIR)lib/$(LIBPTHREAD); \ +		install -m 644 $(LIBPTHREAD) $(TOPDIR)lib; \ +	fi; + +$(LIBTHREAD_DB): subdirs +	@if [ -f $(LIBTHREAD_DB) ] ; then \ +		set -e; \ +		install -d $(TOPDIR)lib; \ +		rm -f $(TOPDIR)lib/$(LIBTHREAD_DB); \ +		install -m 644 $(LIBTHREAD_DB) $(TOPDIR)lib; \ +	fi; -ar-target: $(OBJS) -	$(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS) -	install -d $(TOPDIR)lib -	rm -f $(TOPDIR)lib/$(LIBPTHREAD) -	install -m 644 $(LIBPTHREAD) $(TOPDIR)lib  $(OBJS): %.o : %.c  	$(CC) $(CFLAGS) -c $< -o $@ @@ -48,15 +66,53 @@ $(OBJS): %.o : %.c  $(OBJ): Makefile  shared: all -	$(LD) $(LDFLAGS) -soname=$(LIBPTHREAD_SHARED).$(MAJOR_VERSION) \ -		-o $(LIBPTHREAD_SHARED_FULLNAME) --whole-archive $(LIBPTHREAD) \ -		--no-whole-archive -L$(TOPDIR)/lib -lc; -	install -d $(TOPDIR)lib -	rm -f $(TOPDIR)lib/$(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib/$(LIBPTHREAD_SHARED).$(MAJOR_VERSION) -	install -m 644 $(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib; -	(cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) $(LIBPTHREAD_SHARED));  -	(cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) $(LIBPTHREAD_SHARED).$(MAJOR_VERSION));  - -clean:  -	rm -f *.[oa] *~ core $(LIBPTHREAD_SHARED)* $(LIBPTHREAD_SHARED_FULLNAME)* +	if [ -f $(LIBPTHREAD) ] ; then \ +		set -e; \ +		$(LD) $(LDFLAGS) -soname=$(LIBPTHREAD_SHARED).$(PT_VERSION) \ +			-o $(LIBPTHREAD_SHARED_FULLNAME) --whole-archive $(LIBPTHREAD) \ +			--no-whole-archive $(TOPDIR)/libc/misc/internals/interp.o \ +			-L$(TOPDIR)/lib -lc; \ +		install -d $(TOPDIR)lib; \ +		rm -f $(TOPDIR)lib/$(LIBPTHREAD_SHARED_FULLNAME) \ +			$(TOPDIR)lib/$(LIBPTHREAD_SHARED).$(PT_VERSION); \ +		install -m 644 $(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib; \ +		(cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) \ +			$(LIBPTHREAD_SHARED)); \ +		(cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) \ +			$(LIBPTHREAD_SHARED).$(PT_VERSION)); \ +	fi; +	if [ -f $(LIBTHREAD_DB) ] ; then \ +		set -e; \ +		$(LD) $(LDFLAGS) -soname=$(LIBTHREAD_DB_SHARED).$(PT_VERSION) \ +			-o $(LIBTHREAD_DB_SHARED_FULLNAME) --whole-archive $(LIBTHREAD_DB) \ +			--no-whole-archive $(TOPDIR)/libc/misc/internals/interp.o \ +			-L$(TOPDIR)/lib -lc; \ +		install -d $(TOPDIR)lib; \ +		rm -f $(TOPDIR)lib/$(LIBTHREAD_DB_SHARED_FULLNAME) \ +			$(TOPDIR)lib/$(LIBTHREAD_DB_SHARED).$(PT_VERSION); \ +		install -m 644 $(LIBTHREAD_DB_SHARED_FULLNAME) $(TOPDIR)lib; \ +		(cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \ +			$(LIBTHREAD_DB_SHARED)); \ +		(cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \ +			$(LIBTHREAD_DB_SHARED).$(PT_VERSION)); \ +	fi; + +tags: +	ctags -R +	 +subdirs: $(patsubst %, _dir_%, $(DIRS)) +subdirs_clean: $(patsubst %, _dirclean_%, $(ALL_SUBDIRS)) + +$(patsubst %, _dir_%, $(DIRS)) : dummy +	$(MAKE) -C $(patsubst _dir_%, %, $@) + +$(patsubst %, _dirclean_%, $(ALL_SUBDIRS)) : dummy +	$(MAKE) -C $(patsubst _dirclean_%, %, $@) clean + +clean: subdirs_clean +	rm -f *.[oa] *~ core $(LIBPTHREAD) $(LIBPTHREAD_SHARED_FULLNAME) \ +		$(LIBTHREAD_DB) $(LIBTHREAD_DB_SHARED_FULLNAME) + +.PHONY: dummy + diff --git a/libpthread/linuxthreads/ChangeLog b/libpthread/linuxthreads/ChangeLog new file mode 100644 index 000000000..0025b87c0 --- /dev/null +++ b/libpthread/linuxthreads/ChangeLog @@ -0,0 +1,1257 @@ +2000-02-22  Ulrich Drepper  <drepper@redhat.com> + +	* semaphore.h (SEM_FAILED): Use 0 not NULL. + +2000-02-14  Ulrich Drepper  <drepper@redhat.com> + +	* condvar.c (pthread_cond_timedwait_relative_old): Tight loop with +	nanosleep does not work either.  Get absolute time inside the +	loop. +	(pthread_cond_timedwait_relative_new): Likewise. +	Patch by Kaz Kylheku <kaz@ashi.footprints.net>. + +2000-02-13  Ulrich Drepper  <drepper@redhat.com> + +	* condvar.c (pthread_cond_timedwait_relative_old): Undo last patch +	but keep the code around.  A bug in the kernel prevent us from +	using the code. +	(pthread_cond_timedwait_relative_new): Likewise. +	(PR libc/1597 and libc/1598). + +2000-02-01  Kaz Kylheku  <kaz@ashi.footprints.net> + +	* condvar.c (pthread_cond_timedwait_relative_old): Do tight +	loop around nanosleep calls instead of around most of the function +	(pthread_cond_timedwait_relative_new): Likewise. +	body.  Got rid of backwards goto and one local. + +2000-01-31  Ulrich Drepper  <drepper@redhat.com> + +	* condvar.c (pthread_cond_timedwait_relative_old): Recompute time +	before every nanosleep call to account for time spent in the rest +	of the function. +	(pthread_cond_timedwait_relative_new): Likewise. +	Patch by khendricks@ivey.uwo.ca (PR libc/1564). + +2000-01-29  Ulrich Drepper  <drepper@redhat.com> + +	* condvar.c (pthread_cond_timedwait_relative_old): Get remaining time +	from nanosleep call so that in case we restart we only wait for the +	remaining time. +	(pthread_cond_timedwait_relative_new): Likewise. +	Patch by khendricks@ivey.uwo.ca (PR libc/1561). + +2000-01-18  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_allocate_stack): Compute guard page address +	correctly.  Patch by HJ Lu. + +2000-01-12  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h (pthread_readlock_info): New structure. +	(_pthread_descr_struct): Add p_readlock_list, p_readlock_free, and +	p_untracked_readlock_count. +	* pthread.c (__pthread_initial_thread, pthread_manager_thread): +	Add initializers for new fields. +	* manager.c (pthread_free): Free read/write lock lists. +	* queue.h (queue_is_empty): New function. +	* rwlock.c: Implement requirements about when readers should get +	locks assigned. +	* sysdeps/pthread/pthread.h +	(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP): New definition. +	* sysdeps/pthread/bits/pthreadtypes.h (struct _pthread_rwlock_t): +	Define this name as well. +	Patches by Kaz Kylheku <kaz@ashi.footprints.net>. + +2000-01-06  Andreas Jaeger  <aj@suse.de> + +	* pthread.c: Remove extra initializer. + +2000-01-05  Ulrich Drepper  <drepper@cygnus.com> + +	* pthread.c (__pthread_initial_thread, pthread_manager_thread): +	Adjust initializers for struct _pthread_descr_struct change. +	* internals.h (struct _pthread_descr_struct): Move new elements to +	the end. + +2000-01-03  Kaz Kylheku  <kaz@ashi.footprints.net> + +	Redesigned how cancellation unblocks a thread from internal +	cancellation points (sem_wait, pthread_join, +	pthread_cond_{wait,timedwait}). +	Cancellation won't eat a signal in any of these functions +	(*required* by POSIX and Single Unix Spec!). +	* condvar.c: Spontaneous wakeup on pthread_cond_timedwait won't eat a +	simultaneous condition variable signal (not required by POSIX +	or Single Unix Spec, but nice). +	* spinlock.c: __pthread_lock queues back any received restarts +	that don't belong to it instead of assuming ownership of lock +	upon any restart; fastlock can no longer be acquired by two threads +	simultaneously. +	* restart.h: Restarts queue even on kernels that don't have +	queued real time signals (2.0, early 2.1), thanks to atomic counter, +	avoiding a rare race condition in pthread_cond_timedwait. + +1999-12-28  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/alpha/pt-machine.h:  Move stack_pointer definition to the +	beginning. + +	* manager.c (__pthread_start): Add one more cast to assignment of +	arg to prevent warning on 64bit machines. + +1999-12-21  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_handle_create): Set p_pid of new thread +	before calling the callback function to report a new thread. + +1999-12-20  Andreas Jaeger  <aj@suse.de> + +	* pthread.c (pthread_initialize): Move getrlimit call after +	setting of errno. + +1999-12-01  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/i386/pt-machine.h: Move stack_pointer definition to the +	beginning. +	* sysdeps/i386/i686/pt-machine.h: Likewise. +	Patches by Alan Modra <alan@SPRI.Levels.UniSA.Edu.Au>. + +1999-11-23  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_start_thread_event): Initialize p_pid already +	here. + +1999-11-22  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h: Add prototype for __pthread_manager_event. +	* manager.c (__pthread_manager_event): New function. +	(pthread_start_thread_event): Correct computation of self. +	Use INIT_THREAD_SELF. +	* pthread.c (__pthread_manager_thread): Initialize p_lock. +	(__pthread_initialize_manager): Respect event flags also for creation +	of the manager thread. + +1999-11-08  Ulrich Drepper  <drepper@cygnus.com> + +	* pthread.c (__pthread_initialize_manager): Initialize +	__pthread_manager_thread.p_tid. + +1999-11-02  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h: Declare __pthread_last_event. +	* manager.c: Define __pthread_last_event. +	(pthread_handle_create): Set __pthread_last_event. +	(pthread_exited): Likewise. +	* join.c (pthread_exit): Likewise. + +	* Makefile (libpthread-routines): Add events. +	* events.c: New file. +	* internals.h: Protect against multiple inclusion. +	Include thread_dbP.h header. +	(struct _pthread_descr_struct): Add new fields p_report_events and +	p_eventbuf. +	Declare event reporting functions. +	* join.c (pthread_exit): Signal event if this is wanted. +	* manager.c (__pthread_threads_events): New variable. +	(pthread_handle_create): Take new parameters with event information. +	Signal TD_CREATE event if wanted. +	(__pthread_manager): Adjust pthread_handle_create call. +	(pthread_start_thread_event): New function.  Block until manager is +	finished and then call pthread_start_thread. +	(pthread_exited): Signal TD_REAP event if wanted. + +1999-10-26  Ulrich Drepper  <drepper@cygnus.com> + +	* restart.h (suspend_with_cancellation): Rewrite as a macro. + +	* condvar.c (pthread_cond_timedwait_relative): Don't mark as inline. + +1999-10-21  Xavier Leroy  <Xavier.Leroy@inria.fr> + +	* linuxthreads/pthread.c: For i386, wrap pthread_handle_sigrestart +	and pthread_handle_sigcancel with functions that restore +	%gs from the signal context.  For each signal handling function, +	two wrappers are required, one for a non-RT signal and one for +	a RT signal. +	* linuxthreads/signal.c: For i386, add code to restore %gs +	from the signal context in pthread_sighandler and +	pthread_sighandler_rt. + +1999-10-09  Andreas Jaeger  <aj@suse.de> + +	* internals.h: Add __new_sem_post to get prototype in +	manager.c; include semaphore.h for needed types. + +1999-10-08  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (__pthread_manager) [REQ_POST]: Use __new_sem_post +	directly instead of calling sem_post which should not be necessary +	but is faster and might help in some case to work around problems. + +1999-09-25  Ulrich Drepper  <drepper@cygnus.com> + +	* condvar.c (pthread_cond_timedwait_relative): Never return with +	EINTR.  Patch by Andreas Schwab. + +1999-09-19  Ulrich Drepper  <drepper@cygnus.com> + +	* signals.c (sigaction): Correct last patch.  Don't select +	pthread_sighandler_rt based on the signal number but instead of +	the SA_SIGINFO flag. + +1999-09-23  Ulrich Drepper  <drepper@cygnus.com> + +	* specific.c: Move definitions of struct pthread_key_struct and +	destr_function to ... +	* internals.h: ...here. + +1999-09-03  Andreas Schwab  <schwab@suse.de> + +	* ptfork.c (__fork): Renamed from fork and use __libc_fork.  Add +	fork as weak alias. +	(__vfork): New function, alias vfork. +	* Versions: Export __fork, vfork, and __vfork in libpthread. + +1999-08-23  Andreas Schwab  <schwab@suse.de> + +	* signals.c (pthread_sighandler): Add SIGCONTEXT_EXTRA_ARGS to +	call to signal handler. + +1999-08-20  Ulrich Drepper  <drepper@cygnus.com> + +	* pthread.c (__pthread_reset_main_thread): Undo last change. +	(__pthread_kill_other_threads_np): Reset signal handlers for the +	signals we used in the thread implementation here. + +1999-08-19  Ulrich Drepper  <drepper@cygnus.com> + +	* pthread.c (__pthread_reset_main_thread): Reset signal handlers +	for the signals we used in the thread implementation [PR libc/1234]. + +	* Versions: Export __pthread_kill_other_threads_np from libpthread +	for GLIBC_2.1.2. + +	* signals.c: Pass sigcontext through wrapper to the user function. + +1999-08-01  Ulrich Drepper  <drepper@cygnus.com> + +	* Versions [ld.so] (GLIBC_2.0): Export __libc_internal_tsd_get and +	__libc_internal_tsd_set. + +1999-07-29  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* manager.c: Remove inclusion of <linux/tasks.h> since it's not +	needed anymore. + +1999-07-16  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* internals.h: Align _pthread_descr_struct to 32 bytes. +	Reported by Tim Hockin <thockin@cobaltnet.com>, close PR libc/1206. + +1999-07-09  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_handle_create): Free mmap region after stack +	if clone failed.  Patch by Kaz Kylheku <kaz@ashi.FootPrints.net>. + +1999-07-09  Cristian Gafton  <gafton@redhat.com> + +	* Makefile (libpthread-routines): Add oldsemaphore routine. +	* Versions: Add sem_destroy, sem_getvalue, sem_init, sem_post, +	sem_trywait, and sem_wait to GLIBC_2.1. +	* oldsemaphore.c: New file. +	* semaphore.c: Add default_symbol_versions for the changed functions. +	(__new_sem_init): Rename from sem_init. +	(__new_sem_post): Rename from sem_post. +	(__new_sem_wait): Rename from sem_wait. +	(__new_sem_trywait): Rename from sem_trywait. +	(__new_sem_getvalue): Rename from sem_getvalue. +	(__new_sem_destroy): Rename from sem_destroy. + +1999-06-23  Robey Pointer  <robey@netscape.com> + +	* internals.h: Added p_nextlock entry to separate queueing for a +	lock from queueing for a CV (sometimes a thread queues on a lock +	to serialize removing itself from a CV queue). +	* pthread.c: Added p_nextlock to initializers. +	* spinlock.c: Changed to use p_nextlock instead of p_nextwaiting. + +1999-05-23  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* man/pthread_cond_init.man: Correct example. +	Reported by Tomas Berndtsson <tomas@nocrew.org>. + +	* linuxthreads.texi (Condition Variables): Likewise. + +1999-05-18  Jakub Jelinek  <jj@ultra.linux.cz> + +	* sysdeps/sparc/sparc64/pt-machine.h (__compare_and_swap): Use +	casx not cas, also successful casx returns the old value in rd +	and not the new value. + +1999-05-16  Xavier Leroy  <Xavier.Leroy@inria.fr> + +	* manager.c: If pthread_create() is given a NULL attribute +	and the thread manager runs with a realtime policy, set the +	scheduling policy of the newly created thread back to SCHED_OTHER. +	* manager.c: If the PTHREAD_INHERIT_SCHED attribute is given, +	initialize the schedpolicy field of new_thread->p_start_args +	to that of the calling thread. + +1999-04-29  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/sparc/sparc64/pt-machine.h (__compare_and_swap): cas +	instruction does not allow memory element to use offset. + +1999-04-28  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_allocate_stack): Optimize initialization of new +	thread descriptor. + +	* sysdeps/pthread/bits/libc-lock.h (__libc_lock_define_initialized): +	Don't use initializer since it is all zeroes. +	(__libc_once_define): Likewise. + +1999-04-16  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* sysdeps/arm/Implies: Removed since cmpxchg/no-cmpxchg +	doesn't exist anymore. +	* sysdeps/i386/Implies: Likewise. +	* sysdeps/m68k/Implies: Likewise. +	* sysdeps/mips/Implies: Likewise. +	* sysdeps/powerpc/Implies: Likewise. +	* sysdeps/sparc/sparc32/Implies: Likewise. +	* sysdeps/sparc/sparc64/Implies: Likewise. + +1999-04-15  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/alpha/bits/semaphore.h: Removed. +	* sysdeps/powerpc/bits/semaphore.h: Removed. +	* sysdeps/pthread/cmpxchg/bits/semaphore.h: Removed. +	* sysdeps/pthread/no-cmpxchg/bits/semaphore.h: Removed. +	* Makefile (headers): Remove bits/semaphore.h. + +	* semaphore.h: Define _pthread_descr if necessary. +	Don't include limits.h.  Define SEM_VALUE_MAX directly. +	Define SEM_FAILED. +	(sem_t): Protect element names with leading __. +	Add declarations for sem_close, sem_open, and sem_unlink. +	* semaphore.c: Adjust all functions for new element names. +	Define sem_close, sem_open, and sem_unlink. +	* Versions (libthread): Add sem_close, sem_open, and sem_unlink for +	GLIBC_2.1.1. +	* sysdeps/pthread/bits/pthreadtypes.h: Define _pthread_descr only if +	necessary. + +1999-03-16  H.J. Lu  <hjl@gnu.org> + +	* specific.c (pthread_key_delete): Check th->p_terminated to see +	if the thread is running. + +	* Versions (__libc_internal_tsd_get, __libc_internal_tsd_set): +	Added to GLIBC_2.0 for libc.so. + +1999-02-12  H.J. Lu  <hjl@gnu.org> + +	* Versions (__libc_current_sigrtmin, __libc_current_sigrtmax, +	__libc_allocate_rtsig): Added to GLIBC_2.1. + +	* internals.h (DEFAULT_SIG_RESTART): Removed. +	(DEFAULT_SIG_CANCEL): Removed. + +	* pthread.c (init_rtsigs, __libc_current_sigrtmin, +	__libc_current_sigrtmax, __libc_allocate_rtsig): New functions. +	(__pthread_sig_restart, __pthread_sig_cancel, +	__pthread_sig_debug): Initialized. +	(pthread_initialize): Call init_rtsigs () to initialize +	real-time signals. + +1999-02-03  H.J. Lu  <hjl@gnu.org> + +	* manager.c (__pthread_manager): Do block __pthread_sig_debug. +	Don't restart the thread which sent REQ_DEBUG. +	(pthread_start_thread): Check if __pthread_sig_debug > 0 +	before debugging. + +	* pthread.c (__pthread_initialize_manager): Suspend ourself +	after sending __pthread_sig_debug to gdb instead of +	__pthread_sig_cancel. + +1999-01-24  H.J. Lu  <hjl@gnu.org> + +	* manager.c (__pthread_manager): Delete __pthread_sig_debug +	from mask if __pthread_sig_debug > 0. +	(pthread_handle_create): Increment __pthread_handles_num. + +	* manager.c (pthread_handle_create): Don't pass CLONE_PTRACE to clone. +	* pthread.c (__pthread_initialize_manager): Likewise. + +	* pthread.c (pthread_initialize): Use __libc_allocate_rtsig (1) +	instead of __libc_allocate_rtsig (2). +	(__pthread_initialize_manager): Send __pthread_sig_debug to gdb +	instead of __pthread_sig_cancel. +	(pthread_handle_sigdebug): Fix comments. + +1999-01-21  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_allocate_stack): Set +	__pthread_nonstandard_stacks if user-specified stack is used. + +1999-01-16  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/unix/sysv/linux/bits/posix_opt.h: Add _LFS_ASYNCHRONOUS_IO, +	_LFS_LARGEFILE, _LFS64_LARGEFILE, and _LFS64_STDIO from Unix98. + +1999-01-07  Xavier Leroy  <Xavier.Leroy@inria.fr> + +	* pthread.c: Use a third signal __pthread_sig_debug distinct +	from __pthread_sig_cancel to notify gdb when a thread is +	created +	* manager.c: Likewise. +	* internals.h: Likewise. +	* signals.c: The implementation of sigwait(s) assumed that +	all signals in s have signal handlers already attached. +	This is not required by the standard, so make it work +	also if some of the signals have no handlers. + +1999-01-05  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de> + +	* linuxthreads.texi: Remove pointers from first @node.  Move old +	@node spec inside comment. + +1998-12-31  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/bits/stdio-lock.h: Define _IO_lock_lock and +	_IO_lock_unlock. + +1998-12-29  Ulrich Drepper  <drepper@cygnus.com> + +	* semaphore.c (sem_trywait): Don't forget to unlock the semaphore +	lock.  Patch by Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>. + +1998-12-21  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c: Threads now send __pthread_sig_cancel on termination. +	Change clone call and signal masks. +	* thread.c (pthread_handle_sigrestart): Remove special code for +	manager. +	(pthread_handle_sigcancel): In manager thread call +	__pthread_manager_sighandler. +	* sysdeps/i386/pt-machine.h (__compare_and_swap): Add memory clobber. +	* sysdeps/i386/i686/pt-machine.h: Likewise. +	Patches by Xavier Leroy. + +1998-12-14  Ulrich Drepper  <drepper@cygnus.com> + +	* spinlock.c (__pthread_unlock): Don't crash if called for an +	untaken mutex.  Reported by Ruslan V. Brushkoff <rus@Snif.Te.Net.UA>. + +	* Examples/ex6.c: Unbuffer stdout and reduce sleep time to reduce +	overall runtime. + +1998-12-13  Ulrich Drepper  <drepper@cygnus.com> + +	* Examples/ex3.c: Wait until all threads are started before +	searching for the number to avoid race condition on very fast +	systems. + +1998-12-08  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* sysdeps/pthread/pthread.h: Remove __pthread_setcanceltype +	declaration since it's not needed. + +	* sysdeps/pthread/pthread.h: Move internal functions to ... +	* internals.h: ...here. + +1998-12-02  H.J. Lu  <hjl@gnu.org> + +	* pthread.c (__pthread_sig_restart): Initiliaze to 0 if +	SIGRTMIN is defined. +	(__pthread_sig_cancel): Likewise. + +1998-12-01  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* wrapsyscall.c: Include <sys/mman.h> for msync, +	<stdlib.h> for system and <termios.h> for tcdrain prototype. +	Correct msync declaration. + +1998-11-29  Roland McGrath  <roland@baalperazim.frob.com> + +	* sysdeps/pthread/bits/libc-tsd.h (__libc_tsd_define, __libc_tsd_get, +	__libc_tsd_set): New macros for new interface. +	* no-tsd.c: New file, provide uninitialized defns of +	__libc_internal_tsd_get and __libc_internal_tsd_set. +	* Makefile (routines): Add no-tsd. + +1998-10-12  Roland McGrath  <roland@baalperazim.frob.com> + +	* internals.h: Include <bits/libc-tsd.h>, not <bits/libc-lock.h>. +	* sysdeps/pthread/bits/libc-lock.h (__libc_internal_tsd_get, +	__libc_internal_tsd_set): Move decls to ... +	* sysdeps/pthread/bits/libc-tsd.h: New file for __libc_internal_tsd_* +	declarations. + +	* sysdeps/pthread/bits/libc-lock.h (__libc_internal_tsd_get, +	__libc_internal_tsd_set): Make these pointers to functions, not +	functions; remove #pragma weak decls for them. +	* specific.c (__libc_internal_tsd_get, __libc_internal_tsd_set): +	Define static functions and initialized pointers to them. + +1998-11-18  Ulrich Drepper  <drepper@cygnus.com> + +	* Makefile (CFLAGS-mutex.c): Define as -D__NO_WEAK_PTHREAD_ALIASES. +	(CFLAGS-specific.c): Likewise. +	(CFLAGS-pthread.c): Likewise. +	(CFLAGS-ptfork.c): Likewise. +	(CFLAGS-cancel.c): Likewise. +	* sysdeps/pthread/bits/libc-lock.h: Don't mark __pthread_* functions +	as weak references if __NO_WEAK_PTHREAD_ALIASES is defined. + +	* mutex.c (pthread_mutex_init): Define as strong symbol. +	(pthread_mutex_destroy): Likewise. +	(pthread_mutex_trylock): Likewise. +	(pthread_mutex_lock): Likewise. +	(pthread_mutex_unlock): Likewise. +	(pthread_mutexattr_init): Likewise. +	(pthread_mutexattr_destroy): Likewise. +	(pthread_once): Likewise. +	* ptfork.c (pthread_atfork): Likewise. +	* specific.c (pthread_key_create): Likewise. +	(pthread_setspecific): Likewise. +	(pthread_getspecific): Likewise. + +1998-11-15  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de> + +	* linuxthreads.texi: Fix punctuation after xref. + +1998-11-10  H.J. Lu  <hjl@gnu.org> + +	* sysdeps/unix/sysv/linux/bits/local_lim.h: Undefine NR_OPEN +	if it is defined in <linux/limits.h>. + +1998-10-29 14:28  Ulrich Drepper  <drepper@cygnus.com> + +	* spinlock.h (__pthread_trylock): Define inline. +	(__pthread_lock): Add extra parameter to declaration.  Declare +	using internal_function. +	(__pthread_unlock): Declare using internal_function. +	* spinlock.c (__pthread_lock): Add new parameter.  Use it instead +	of local variable self.  Avoid recomputing self.  Define using +	internal_function. +	(__pthread_trylock): Remove. +	(__pthread_unlock): Define using internal_function. +	* cancel.c: Adjust for __pthread_lock interface change.  Use already +	computed self value is possible. +	* condvar.c: Likewise. +	* join.c: Likewise. +	* manager.c: Likewise. +	* mutex.c: Likewise. +	* pthread.c: Likewise. +	* rwlock.c: Likewise. +	* semaphore.c: Likewise. +	* signals.c: Likewise. + +1998-10-27 13:46  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/pthread.h (struct _pthread_cleanup_buffer): Prepend +	__ to field names of the struct. +	* sysdeps/pthread/bits/pthreadtypes.h (struct _pthread_fastlock): +	Likewise. +	(pthread_attr_t): Likewise. +	(pthread_cond_t): Likewise. +	(pthread_condattr_t): Likewise. +	(pthread_mutex_t): Likewise. +	(pthread_mutexattr_t): Likewise. +	(pthread_rwlock_t): Likewise. +	(pthread_rwlockattr_t): Likewise. +	* attr.c: Adjust for pthread.h and pthreadtypes.h change. +	* cancel.c: Likewise. +	* condvar.c: Likewise. +	* manager.c: Likewise. +	* mutex.c: Likewise. +	* pthread.c: Likewise. +	* ptlongjmp.c: Likewise. +	* rwlock.c: Likewise. +	* spinlock.c: Likewise. + +1998-10-09  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/i386/pt-machine.h (get_eflags, set_eflags): Mark these +	also with PT_EI. + +	* sysdeps/i386/i686/pt-machine.h: Remove unused inline +	definitions. + +	* Makefile (libpthread-routines): Add pt-machine. +	* pt-machine.c: New file. +	* sysdeps/alpha/pt-machine.h: Define PT_EI as extern inline is not +	yet defined.  Use PT_EI in extern inline definitions. +	* sysdeps/arm/pt-machine.h: Likewise. +	* sysdeps/i386/pt-machine.h: Likewise. +	* sysdeps/i386/i686/pt-machine.h: Likewise. +	* sysdeps/m68k/pt-machine.h: Likewise. +	* sysdeps/mips/pt-machine.h: Likewise. +	* sysdeps/powerpc/pt-machine.h: Likewise. +	* sysdeps/sparc/sparc32/pt-machine.h: Likewise. +	* sysdeps/sparc/sparc64/pt-machine.h: Likewise. + +1998-10-02  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* semaphore.h: Include <sys/types.h> so that _pthread_descr +	is declared. + +1998-09-15  David S. Miller  <davem@pierdol.cobaltmicro.com> + +	* sysdeps/sparc/sparc32/pt-machine.h (INIT_THREAD_SELF): Add nr +	argument. +	* sysdeps/sparc/sparc64/pt-machine.h (INIT_THREAD_SELF): Likewise. + +1998-09-12 14:24 -0400  Zack Weinberg  <zack@rabi.phys.columbia.edu> + +	* linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h: Add +	multiple inclusion guard. + +1998-09-02 11:08  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* signals.c (sigaction): Check that sig is less than NSIG to avoid +	array index overflow. + +1998-09-06 10:56  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/semaphore.h: New file. + +1998-09-06 09:08  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/bits/libc-lock.h (enum __libc_tsd_key_t): Add +	_LIBC_TSD_KEY_DL_ERROR. + +1998-08-31  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/i386/i686/pt-machine.h (testandset): Add memory clobber. +	* sysdeps/i386/pt-machine.h: Likewise. +	Suggested by Roland McGrath. + +1998-08-28 13:58  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h: Also define THREAD_GETMEM_NC and THREAD_SETMEM_NC to +	access thread data with non-constant offsets. +	* specific.c: Use THREAD_GETMEM_NC and THREAD_SETMEM_NC where +	necessary. + +	* sysdeps/i386/useldt.h: Fix typo.  Add THREAD_GETMEM_NC and +	THREAD_SETMEM_NC definitions. + +	* sysdeps/sparc/sparc32/pt-machine.h: Define THREAD_GETMEM_NC and +	THREAD_SETMEM_NC. +	* sysdeps/sparc/sparc64/pt-machine.h: Likewise. + +1998-08-26 15:46  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h: Define THREAD_GETMEM and THREAD_SETMEM to default if +	not already defined. +	(struct _pthread_descr_struct): Add p_self and p_nr field. +	* manager.c (__pthread_handles): Define second element to point +	to manager thread. +	(__pthread_handles_num): Initialize to 2. +	(__pthread_manager): Use INIT_THREAD_SELF with two arguments. +	(pthread_start_thread): Likewise. +	(pthread_handle_create): Start search for free slot at entry 2. +	Initialize new fields p_self and p_nr. +	Call __clone with CLONE_PTRACE if available. +	(pthread_free): Call FREE_THREAD_SELF if available. +	* pthread.c (__pthread_initial_thread): Initialize new fields. +	(__pthread_manager_thread): Likewise. +	(__pthread_initialize_manager): Call __clone with CLONE_PTRACE. + +	* cancel.c: Use THREAD_GETMEM and THREAD_SETMEM to access the +	elements of the thread descriptor. +	* condvar.c: Likewise. +	* errno.c: Likewise. +	* join.c: Likewise. +	* manager.c: Likewise. +	* pthread.c: Likewise. +	* ptlongjmp.c: Likewise. +	* semaphore.c: Likewise. +	* signals.c: Likewise. +	* specific.c: Likewise. +	* spinlock.c: Likewise. + +	* sysdeps/alpha/pt-machine.h (INIT_THREAD_SELF): Add extra parameter. + +	* sysdeps/i386/useldt.h: New file. +	* sysdeps/i386/i686/pt-machine.h: Show how to use this file. + +	* sysdeps/sparc/sparc32/pt-machine.h: Define THREAD_GETMEM and +	THREAD_SETMEM using __thread_self. +	* sysdeps/sparc/sparc64/pt-machine.h: Likewise. + +1998-08-24  Geoff Keating  <geoffk@ozemail.com.au> + +	* spinlock.c (__pthread_lock): Reset p_nextwaiting to NULL if it +	turned out that we didn't need to queue after all. + +1998-08-22  Geoff Keating  <geoffk@ozemail.com.au> + +	* sysdeps/powerpc/pt-machine.h: Remove testandset, it's not used +	and wastes space; correct types. + +1998-08-08 11:18  H.J. Lu  <hjl@gnu.org> + +	* signals.c (sigaction): Handle NULL argument. + +1998-08-04  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/unix/sysv/linux/bits/sigthread.h: Use __sigset_t instead +	of sigset_t. + +1998-08-02  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* Makefile (linuxthreads-version): Extract correct number from +	Banner. + +1998-07-29  Xavier Leroy  <Xavier.Leroy@inria.fr> + +	* Banner: Bump version number to 0.8 +	* FAQ.html: Many updates, in particular w.r.t. debugging. +	* manager.c: Support for non-default stacksize for +	LinuxThreads-allocated stacks; +	don't use guard pages for stacks with default size, rely on +	rlimit(RLIMIT_STACK) instead (it's cheaper). +	* attr.c: Likewise. +	* cancel.c: Use __pthread_sig_cancel and __pthread_sig_restart +	everywhere instead of PTHREAD_SIG_CANCEL and PTHREAD_SIG_RESTART. +	* condvar.c: Likewise. +	* internals.h: Likewise. +	* restart.h: Likewise. +	* signals.c: Likewise. +	* pthread.c: Likewise; set rlimit(RLIMIT_STACK) as we need it. + +1998-07-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* weaks.c: Define pthread_mutexattr_[sg]ettype instead of +	__pthread_mutexattr_[sg]ettype.  Add more weak aliases. +	* Versions: Put __pthread_mutexattr_settype under version +	GLIBC_2.0.  Don't export __pthread_mutexattr_setkind_np and +	__pthread_mutexattr_gettype. + +1998-07-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* sysdeps/pthread/bits/libc-lock.h: Make +	__pthread_mutexattr_settype weak.  Don't make +	__pthread_mutexattr_setkind_np weak. + +1998-07-16 10:52  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_handle_create): Check whether sched_setscheduler +	call can succeed here. + +	* mutex.c: Define __pthread_mutexattr_settype and make +	__pthread_mutexattr_setkind_np an alias. +	Likewise for __pthread_mutexattr_gettype. + +1998-07-15 11:00 -0400  Zack Weinberg  <zack@rabi.phys.columbia.edu> + +	* attr.c (pthread_attr_setschedpolicy): Don't check whether caller +	is root. + +1998-07-14 19:38  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/bits/libc-lock.h: Define __libc_cleanup_end. + +1998-07-11  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* Examples/ex6.c: Include <unistd.h> for usleep. + +1998-06-13 11:04  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* Examples/ex4.c (main): Use exit, not pthread_exit. + +1998-07-09 13:39  Ulrich Drepper  <drepper@cygnus.com> + +	* Versions: Add __pthread_mutexattr_gettype and +	__pthread_mutexattr_settype. +	* lockfile.c: Use __pthread_mutexattr_settype instead of +	__pthread_mutexattr_setkind_np. +	* mutex.c: Define __pthread_mutexattr_gettype and +	__pthread_mutexattr_settype. +	* weak.c: Likewise. +	* sysdeps/pthread/pthread.h: Declare __pthread_mutexattr_gettype and +	__pthread_mutexattr_settype. +	* sysdeps/pthread/bits/libc-lock.h (__libc_lock_init_recursive): +	Use __pthread_mutexattr_settype. + +1998-07-08 22:26  Ulrich Drepper  <drepper@cygnus.com> + +	* Versions: Add pthread_mutexattr_gettype, pthread_mutexattr_settype. +	* mutex.c: Define weak alias pthread_mutexattr_gettype and +	pthread_mutexattr_settype. +	* sysdeps/pthread/pthread.h: Declare these functions. +	Move pthread_sigmask and pthread_kill declaration in separate header. +	* sysdeps/unix/sysv/linux/bits/sigthread.h: New file. + +1998-07-07 15:20  Ulrich Drepper  <drepper@cygnus.com> + +	* Makefile: Add rules to compile and run tests. +	* Examples/ex1.c: Little changes to fix warnings. +	* Examples/ex2.c: Likewise. +	* Examples/ex3.c: Likewise. +	* Examples/ex4.c: Likewise. +	* Examples/ex5.c: Likewise. +	* Examples/ex6.c: New file. + +1998-07-05 11:54  Ulrich Drepper  <drepper@cygnus.com> + +	* Versions: Add pthread_attr_init to GLIBC_2.1 version in libc. + +1998-07-01  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* attr.c: Include <string.h>. + +1998-06-30 11:47  Ulrich Drepper  <drepper@cygnus.com> + +	* attr.c: Include errno.h.  Use memcpy to copy sched_param. +	* internals.h: Include limits.h. +	* manager.c: Use memcpy to copy sched_param. +	* ptfork.c: Include errno.h. +	* pthread.c: Likewise. +	* semaphore.c: Likewise. +	* specific.c: Likewise. +	* spinlock.h: Likewise. +	* sysdeps/pthread/pthread.h: Include only allowed headers.  Move +	type definition to ... +	* sysdeps/pthread/bits/pthreadtypes.h: ...here.  New file. + +1998-06-29 12:34  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/pthread.h: Use __PMT not __P for function pointers. + +	* sysdeps/pthread/pthread.h: Define various PTHREAD_* symbols also +	as macros as demanded in POSIX.1, Annex C. + +1998-06-29 12:29  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h (struct pthread_request): For free use pthread_t +	instead of pthread_descr. +	* join.c (pthread_join): Pass thread_id, not th to manager. +	(pthread_detach): Likewise. +	* manager.c (__pthread_manager): Except thread ID in FREE_REQ case. +	(pthread_exited): Remove detached queue code. +	(pthread_handle_free): Expect thread ID parameter and use it to +	validate the thread decsriptor.  Don't use detached queue. +	Patches by Xavier Leroy. + +1998-06-27  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* libpthread.map: Export accept, longjmp, sigaction, siglongjmp, +	_IO_flockfile, _IO_ftrylockfile, _IO_funlockfile, +	__pthread_atfork, __pthread_key_create, __pthread_once. +	* internals.h: Doc fix. +	* pthread.c (__pthread_initialize): Define again. + +1998-06-26  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_exited): If thread is not detached put it on +	special list. +	(pthread_handle_free): If thread is not on list with living threads +	search on list with detached threads. + +	* sysdeps/pthread/pthread.h (PTHREAD_RWLOCK_INITIALIZER): Correct +	for new definition of pthread_rwlock_t. + +	* spinlock.c: Correct test whether to compile +	__pthread_compare_and_swap or not. + +1998-06-25 19:27  Ulrich Drepper  <drepper@cygnus.com> + +	* attr.c: Finish user stack support.  Change locking code to be safe +	in situations with different priorities. +	* cancel.c: Likewise. +	* condvar.c: Likewise. +	* internals.h: Likewise. +	* join.c: Likewise. +	* manager.c: Likewise. +	* mutex.c: Likewise. +	* pthread.c: Likewise. +	* ptlongjmp.c: Likewise. +	* queue.h: Likewise. +	* rwlock.c: Likewise. +	* semaphore.c: Likewise. +	* semaphore.h: Likewise. +	* signals.c: Likewise. +	* spinlock.c: Likewise. +	* spinlock.h: Likewise. +	* sysdeps/pthread/pthread.h: Likewise. +	Patches by Xavier Leroy. + +	* sysdeps/i386/i686/pt-machine.h: New file. + +1998-06-25  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/pthread.h: Make [sg]et_stacksize and +	[sg]et_stackaddr prototypes always available. + +	* sysdeps/unix/sysv/linux/bits/posix_opt.h: Define +	_POSIX_THREAD_ATTR_STACKSIZE and _POSIX_THREAD_ATTR_STACKADDR. + +1998-06-24  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_free): Undo patch from 980430. +	Reported by David Wragg <dpw@doc.ic.ac.uk>. + +1998-06-09 15:07  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c: Define __pthread_manager_adjust_prio and use it to +	increase priority when needed. +	* internals.h: Add prototype for __pthread_manager_adjust_prio. +	* mutex.c: Optimize mutexes to wake up only one thread. +	* pthread.c: Move PID of manager for global variable in structure +	element. +	Patches by Xavier Leroy. + +1998-06-07 13:47  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/pthread/bits/libc-lock.h: Optimize cleanup handlers a bit. + +1998-06-03  Andreas Jaeger  <aj@arthur.rhein-neckar.de> + +	* attr.c: Correct typo. + +1998-05-01  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_free): Unmap guard before the stack. +	Patch by Matthias Urlichs. + +1998-04-30  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c (pthread_free): Detect already free child. +	Patch by Xavier Leroy, reported by Matthias Urlichs. + +1998-04-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* Makefile (linuxthreads-version): Renamed back from +	libpthread-version. + +1998-04-21  Ulrich Drepper  <drepper@cygnus.com> + +	* ptlongjmp.c: Add prototypes for __libc_siglongjmp and +	__libc_longjmp. + +1998-04-20 14:55  Ulrich Drepper  <drepper@cygnus.com> + +	* Makefile (libpthread-routines): Add ptlongjmp and spinlock. +	* internals.h: Add definitions for new spinlock implementation. +	* ptlongjmp.c: New file. +	* spinlock.c: New file. +	* spinlock.h (acquire): Don't reschedule using __sched_yield, use +	new function __pthread_acquire to prevent deadlocks with thread +	with different priorities. +	Patches by Xavier Leroy <Xavier.Leroy@inria.fr>. + +1998-03-16  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* manager.c (__pthread_manager): Reduce first argument to select +	to include just the needed file descriptor. + +1998-03-17 00:06  Ulrich Drepper  <drepper@cygnus.com> + +	* manager.c: Fix last patch which caused core dumps. + +	* pthread.c: Correctly handle missing SIGRTMIN. + +1998-03-15  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* libpthread.map: Add __libc_internal_tsd_get and +	__libc_internal_tsd_set.  Add missing cancelable functions. Export +	libc internal versions of the cancelable functions. + +1998-03-13 16:51  Ulrich Drepper  <drepper@cygnus.com> + +	* weaks.c: Define pthread_attr_init as GLIBC_2.0 and GLIBC_2.1. + +1998-03-13 00:46  Ulrich Drepper  <drepper@cygnus.com> + +	* attr.c: Implement pthread_attr_[gs]etguardsize, +	pthread_attr_[gs]setstackaddr, pthread_attr_[gs]etstacksize. +	Change pthread_attr_init to have two interfaces. +	* internals.h (struct _pthread_descr_struct): Add new fields for +	above functions. +	* libpthread.map: Add names in GLIBC_2.1 section. +	* manager.c (pthread_handle_create): Implement guardsize and +	user stack. +	(pthread_free): Likewise. +	* pthread.c (pthread_create): Add new interface for changed +	pthread_attr_t. +	* sysdeps/pthread/pthread.h: Add prototypes for new functions. +	* sysdeps/unix/sysv/linux/bits/local_lim.h: Add definition of +	PTHREAD_STACK_MIN. + +1998-03-11 00:42  Wolfram Gloger  <wmglo@dent.med.uni-muenchen.de> + +	* manager.c: Enable resetting of the thread scheduling policy +	to SCHED_OTHER when the parent thread has a different one. + +1998-02-01 13:51  Ulrich Drepper  <drepper@cygnus.com> + +	* sysdeps/unix/sysv/linux/bits/posix_opt.h: Define +	_POSIX_ASYNCHRONOUS_IO. + +	* sysdeps/pthread/pthread.h: Define bits for Unix98 variants of +	mutexes. +	* mutex.c: Implement new mutex types. + +	* internals.h: Include <signal.h>. + +	* libpthread.map: Add __erno_location and __h_errno_location. + +	* errno.c: Return pointer to variable actually in use.  This might +	not be the one in the thread structure. +	* internals.h (struct _pthread_descr_struct): Add new fields p_errnop +	and p_h_errnop. +	* manager.c (__pthread_manager): Set p_errnop and p_h_errnop member +	of manager thread structure. +	(pthread_handle_create): Set p_errnop and p_h_errnop members for new +	thread. +	* pthread.c: Adapt initializer for thread structures. +	(__pthread_initial_thread): Set p_errnop and p_h_errnop member. +	(__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of +	current thread to global variables. + +1998-01-31 17:27  Ulrich Drepper  <drepper@cygnus.com> + +	* rwlock.c: New file. +	* Makefile (libpthread-routines): Add rwlock. +	* sysdeps/pthread/pthread.h: Define data structures and declare +	functions. +	* libpthread.map: Add new functions. + +1997-12-18 13:50  Philip Blundell  <pb@nexus.co.uk> + +	* sysdeps/arm/pt-machine.h: New file; add ARM support. +	* sysdeps/arm/Implies: likewise. +	* README: Document it. + +1997-12-13  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* signals.c: Remove unneeded initializer for sigwaited, saving a +	warning. + +1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* semaphore.c (sem_init): Set sem_spinlock only if available. + +1997-12-04 01:48  Ulrich Drepper  <drepper@cygnus.com> + +	* mutex.c: Implement PTHREAD_MUTEX_CHECKERROR. +	* sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR. + +	* Makefile: Update from LinuxThreads 0.7. +	* internals.h. Likewise. +	* manager.c: Likewise. +	* mutex.c: Likewise. +	* pthread.c: Likewise. +	* signals.c: Likewise. +	* specific.c: Likewise. +	* Examples/ex3.c: Likewise. + +1997-11-20 18:13  Ulrich Drepper  <drepper@cygnus.com> + +	* pthread.c (__pthread_reset_main_thread): Close pipe only if still +	open. + +1997-10-29 05:38  Ulrich Drepper  <drepper@cygnus.com> + +	* wrapsyscall.c: Add socket functions which are also cancelation +	points. + +1997-10-19 21:40  Wolfram Gloger  <wg@wolfram.dent.med.uni-muenchen.de> + +	* specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get): +	New functions for fast thread specific data within libc. + +	* internals.h: Add new array p_libc_specific to struct +	_pthread_descr_struct. + +	* sysdeps/pthread/bits/libc-lock.h: Declare new functions. + +1997-10-13 05:39  Ulrich Drepper  <drepper@cygnus.com> + +	* semaphore.h: Add __BEGIN_DECLS/__END_DECLS. +	Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>. + +1997-08-29 03:05  Ulrich Drepper  <drepper@cygnus.com> + +	* internals.h (struct _pthread_descr_struct): Add definitions for +	two-level specific key handling. +	* manager.c (pthread_handle_create): Initialize specific memory array. +	* specific.c: Implement two-level key handling. +	* weaks.c: Don't provide dummy key handling. +	* sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define). +	Add definition of __libc_key_t. +	* sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX +	as 1024. +	Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and +	PTHREAD_DESTRUCTOR_ITERATIONS. + +	* manager.c (pthread_handle_create): Compare mmap result with +	MAP_FAILED. + +	* ptfork.c: Rename to __pthread_atfork and make old name a weak alias. +	* sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork. + +1997-08-22 19:04  Richard Henderson  <rth@cygnus.com> + +	sysdeps/sparc -> sysdeps/sparc/sparc32 +	sysdeps/sparc64 -> sysdeps/sparc/sparc64 + +	* internals.h: Change definition of THREAD_SELF to be an expression, +	not a statement that did a return. +	* sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly. +	* sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF): +	Follow Solaris and use a "system reserved" register (%g6) to hold +	the thread descriptor. +	* sysdeps/sparc/sparc64/pt-machine.h: Likewise. + +1997-08-03 00:09  Ulrich Drepper  <drepper@cygnus.com> + +	* mutex.c: Correct pthread_once.  Patch by Xavier Leroy. +	* sysdeps/pthread/pthread.h: Add prototype for __pthread_once. +	* sysdeps/pthread/bits/pthread.h: Add macros for __libc_once. + +	* semaphore.c: Include spinlock.h only when needed. + +	* specific.c (__pthread_setsepcific, __pthread_getspecific): Reject +	keys for entries not in use. + +	* weaks.c: Implement key handling functions for real. + +1997-06-29  01:04  Richard Henderson  <richard@gnu.ai.mit.edu> + +	Initial sparc64-linux support: +	* linuxthreads/sysdeps/sparc64/Implies: New file. +	* linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise. + +1997-06-29 00:48  Ulrich Drepper  <drepper@cygnus.com> + +	* semaphore.c: Include spinlock.h at correct place. +	Patch by HJ Lu. + +1997-06-13 10:06  Richard Henderson  <rth@tamu.edu> + +	The Great Bit File Move: +	* sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h. +	* sysdeps/powerpc/semaphorebits.h: Likewise. +	* sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise. +	* sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise. +	* sysdeps/pthread/libc-lock.h: -> bits/ +	* sysdeps/pthread/stdio-lock.h: Likewise. +	* sysdeps/unix/sysv/linux/local_lim.h: Likewise. +	* sysdeps/unix/sysv/linux/posix_opt.h: Likewise. +	* semaphore.h: Likewise. +	* sysdeps/pthread/pthread.h: Likewise. + +	* lockfile.c: <foo.h> -> <bits/foo.h>. +	* semaphore.h: Likewise. + +	* Makefile: (headers): foo.h -> bits/foo.h. +	* sysdeps/pthread/Makefile: Likewise. + +1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de> + +	* semaphore.c (sem_init): Set sem_spinlock only if available. + +	* sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix +	asm constraints. + +1997-04-09 03:00  Ulrich Drepper  <drepper@cygnus.com> + +	Update from LinuxThreads 0.6. + +	* attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max +	and __sched_get_priority_min instead of names without `__'. + +	* manager.c: Rewrite large parts to implement opaque pthread_t. + +	* cancel.c: Adapt for opaque pthread_t type. +	* condvar.c: Likewise. +	* errno.c: Likewise. +	* join.c: Likewise. +	* mutex.c: Likewise. +	* pthread.c: Likewise. +	* signals.c: Likewise. +	* specific.c: Likewise. +	* restart.h: Likewise. +	* queue.h: Likewise. +	* Examples/ex3.c: Likewise. +	* Examples/ex4.c: Likewise. +	* sysdeps/pthread/pthread.h: Likewise. + +	* pthread.c: Accumulate time for all threads in thread manager. + +	* semaphore.c: Implement fallback implementation for architectures +	sometimes missing compare-exchange operations. + +	* cancel.c (pthread_cancel): Validate handle argument. +	* join.c (pthread_join): Likewise. +	(pthread_detach): Likewise. +	* signals.c (pthread_kill): Likewise. + +	* spinlock.h (acquire): Use __sched_yield not sched_yield. + +	* queue.h (enqueue): Enqueue thread according to priority. + +	* internals.c (struct pthread_start_args): New struct for passing +	args to cloning function. +	(struct _pthread): Rename to _pthread_descr_struct and adapt for +	opaque pthread_t. + +	* Examples/Makefile (clean): Pass -f option to rm. + +	* sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction +	and define TEST_FOR_COMPARE_AND_SWAP. +	* sysdeps/i386/i486/pt-machine.h: Removed. + +	* sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase +	to 1024. + +1997-04-04 16:38  Ulrich Drepper  <drepper@cygnus.com> + +	* restart.h (suspend): Clear p_signal before suspending. +	(suspend_with_cancellation): Likewise. +	Patch by Xavier Leroy <Xavier.Leroy@inria.fr>. + +	* weaks.c: Make __pthread_key_create return 1. +	* sysdeps/pthread/libc-lock.h: Define __libc_key_create, +	__libc_getspecific, __libc_setspecific, and __libc_key_t. +	* sysdeps/pthread/stdio-lock.h: Don't care for implementation not +	using libio. + +1997-03-19 15:13  Miguel de Icaza  <miguel@nuclecu.unam.mx> + +	* sysdeps/sparc/pt-machine (RELEASE): Fix. + +1997-03-01 07:55  Geoff Keating  <geoffk@ozemail.com.au> + +	* sysdeps/powerpc/Implies: Added. +	* sysdeps/powerpc/pt-machine.h: Added. +	* sysdeps/powerpc/semaphorebits.h: Added. + +1997-01-22 01:22  Ulrich Drepper  <drepper@cygnus.com> + +	* linuxtheads/pthread.c (__pthread_initial_thread): Correct +	initializer. +	(__pthread_manager_thread): Likewise. +	Reported by Andreas Jaeger. + +1997-01-18 22:15  Richard Henderson  <rth@tamu.edu> + +	Since sigset_t no longer fits in a register, we can't pass in the +	thread's initial mask so easily.  Take this opportunity to simplify +	the clone implementation by only accepting a single void* argument. + +	* linuxthreads/manager.c (__pthread_manager): Put thread vitals +	in the thread struct instead of as arguments through clone. +	(pthread_start_thread): Look for them there. +	* linuxthreads/internals.h (struct _pthread): Add p_initial_fn, +	p_initial_fn_arg, p_initial_mask.  Fix __pthread_manager proto. +	* linuxthreads/pthread.c (pthread_initialize_manager): Revise +	clone invocation. diff --git a/libpthread/linuxthreads/Changes b/libpthread/linuxthreads/Changes new file mode 100644 index 000000000..8ec26c9a6 --- /dev/null +++ b/libpthread/linuxthreads/Changes @@ -0,0 +1,73 @@ +Release 0.7: +- Destructors for thread-specific data now conform to the POSIX semantics +  (call destructors again if non-NULL TSD remains after a round of +   destruction). +- Implemented thread-specific data as a sparse array, allows more TSD keys +  and smaller thread descriptors (Ulrich Drepper). +- Added "error checking" mutexes. +- Protect against multiple sigwait() on the same signals. +- Simplified implementation of semaphores when compare_and_swap is  +  not available. +- Fixed bug in fork() where stdin was closed if fork() was called before +  the first pthread_create(). +- Fixed bug in the gethostby*_r functions (bad result if null bytes +  in addresses). +- Typos in manual pages corrected. +- First cut at a PowerPC port (not working yet, runs into problems +  with gcc and with the C library). + +Release 0.6: +- Validation of thread identifiers: no more crashes when operating on +  a thread that has exited (based on Pavel Krauz's ideas). +- Added fallback implementation of semaphores for the 386 and the +  Sparc.  +- Fixed a bug in signal handling causing false restarts of suspended +  threads. +- Fixed a bug in realtime scheduling causing all threads to have +  default scheduling on Ix86 with libc5. +- With realtime scheduling, unlocking a mutex now restarts the +  highest priority thread waiting on the mutex, not the +  first-suspended thread (Richard Neitzel). +- Timing a process now returns cumulative times for all threads, not +  just times for the initial thread (suggested by Wolfram Gloger). +- Cleaned up name space (internal defs prefixed by __, weak aliases +  for non-portable extensions). +- MIPS port (contributed by Ralf Baechle). + +Release 0.5: +- Signal-safe semaphores a la POSIX 1003.1b added. +- Locking bug in pthread_mutex_trylock over recursive mutexes fixed. +- Race conditions in thread cancellation fixed. +- Sparc port (contributed by Miguel de Icaza). +- Support for getpwnam_r and getpwuid_r. +- Added pthread_kill_other_threads_np to be used in conjunction with +  exec*(). + +Release 0.4: +- Manual pages for all functions. +- Synchronization bug causing accumulation of zombie processes fixed. +- Race condition in pthread_cond_timedwait fixed. +- Recursive mutexes are back by popular demand. +- Partial support for realtime scheduling (initiated by Richard Neitzel). +- pthread.h cleaned up a lot: now C++ compatible, added missing "const"  +  qualifiers, added short documentation, put to GNU libc standards +  for name space pollution (Ulrich Drepper). +- Motorola 68k port (contributed by Andreas Schwab). +- Interaction with fork(2) cleaned up a lot. + +Release 0.3: +- Thread creation and reclaimation now performed by a centralized +  "thread manager" thread. +- Removed recursive mutexes to make regular mutexes more efficient. +- Now available as a shared library (contributed by Richard Henderson). +- Alpha port (contributed by Richard Henderson). +- Fixed many small discrepancies with Posix 1003.1c. +- Put under the LGPL instead of the GPL. + +Release 0.2: +- Reentrant libc functions (adapted from libc 5.3.9 by Peeter Joot) +- pthread_cond_wait did not reacquire the mutex correctly on return +- More efficient pthread_cond_broadcast + +Release 0.1: +- First public release diff --git a/libpthread/linuxthreads/FAQ.html b/libpthread/linuxthreads/FAQ.html new file mode 100644 index 000000000..21be33ec4 --- /dev/null +++ b/libpthread/linuxthreads/FAQ.html @@ -0,0 +1,1039 @@ +<HTML> +<HEAD> +<TITLE>LinuxThreads Frequently Asked Questions</TITLE> +</HEAD> +<BODY> +<H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR> +                 (with answers)</H1> +<H2 ALIGN=center>[For LinuxThreads version 0.8]</H2> + +<HR><P> + +<A HREF="#A">A. The big picture</A><BR> +<A HREF="#B">B. Getting more information</A><BR> +<A HREF="#C">C. Issues related to the C library</A><BR> +<A HREF="#D">D. Problems, weird behaviors, potential bugs</A><BR> +<A HREF="#E">E. Missing functions, wrong types, etc</A><BR> +<A HREF="#F">F. C++ issues</A><BR> +<A HREF="#G">G. Debugging LinuxThreads programs</A><BR> +<A HREF="#H">H. Compiling multithreaded code; errno madness</A><BR> +<A HREF="#I">I. X-Windows and other libraries</A><BR> +<A HREF="#J">J. Signals and threads</A><BR> +<A HREF="#K">K. Internals of LinuxThreads</A><P> + +<HR> +<P> + +<H2><A NAME="A">A. The big picture</A></H2> + +<H4><A NAME="A.1">A.1: What is LinuxThreads?</A></H4> + +LinuxThreads is a Linux library for multi-threaded programming. +It implements the Posix 1003.1c API (Application Programming +Interface) for threads.  It runs on any Linux system with kernel 2.0.0 +or more recent, and a suitable C library (see section <A HREF="C">C</A>). +<P> + +<H4><A NAME="A.2">A.2: What are threads?</A></H4> + +A thread is a sequential flow of control through a program. +Multi-threaded programming is, thus, a form of parallel programming +where several threads of control are executing concurrently in the +program.  All threads execute in the same memory space, and can +therefore work concurrently on shared data.<P> + +Multi-threaded programming differs from Unix-style multi-processing in +that all threads share the same memory space (and a few other system +resources, such as file descriptors), instead of running in their own +memory space as is the case with Unix processes.<P> + +Threads are useful for two reasons.  First, they allow a program to +exploit multi-processor machines: the threads can run in parallel on +several processors, allowing a single program to divide its work +between several processors, thus running faster than a single-threaded +program, which runs on only one processor at a time.  Second, some +programs are best expressed as several threads of control that +communicate together, rather than as one big monolithic sequential +program.  Examples include server programs, overlapping asynchronous +I/O, and graphical user interfaces.<P> + +<H4><A NAME="A.3">A.3: What is POSIX 1003.1c?</A></H4> + +It's an API for multi-threaded programming standardized by IEEE as +part of the POSIX standards.  Most Unix vendors have endorsed the +POSIX 1003.1c standard.  Implementations of the 1003.1c API are +already available under Sun Solaris 2.5, Digital Unix 4.0, +Silicon Graphics IRIX 6, and should soon be available from other +vendors such as IBM and HP.  More generally, the 1003.1c API is +replacing relatively quickly the proprietary threads library that were +developed previously under Unix, such as Mach cthreads, Solaris +threads, and IRIX sprocs.  Thus, multithreaded programs using the +1003.1c API are likely to run unchanged on a wide variety of Unix +platforms.<P> + +<H4><A NAME="A.4">A.4: What is the status of LinuxThreads?</A></H4> + +LinuxThreads implements almost all of Posix 1003.1c, as well as a few +extensions.  The only part of LinuxThreads that does not conform yet +to Posix is signal handling (see section <A HREF="#J">J</A>).  Apart +from the signal stuff, all the Posix 1003.1c base functionality, +as well as a number of optional extensions, are provided and conform +to the standard (to the best of my knowledge). +The signal stuff is hard to get right, at least without special kernel +support, and while I'm definitely looking at ways to implement the +Posix behavior for signals, this might take a long time before it's +completed.<P> + +<H4><A NAME="A.5">A.5: How stable is LinuxThreads?</A></H4> + +The basic functionality (thread creation and termination, mutexes, +conditions, semaphores) is very stable.  Several industrial-strength +programs, such as the AOL multithreaded Web server, use LinuxThreads +and seem quite happy about it.  There used to be some rough edges in +the LinuxThreads / C library interface with libc 5, but glibc 2 +fixes all of those problems and is now the standard C library on major +Linux distributions (see section <A HREF="#C">C</A>). <P> + +<HR> +<P> + +<H2><A NAME="B">B.  Getting more information</A></H2> + +<H4><A NAME="B.1">B.1: What are good books and other sources of +information on POSIX threads?</A></H4> + +The FAQ for comp.programming.threads lists several books: +<A HREF="http://www.serpentine.com/~bos/threads-faq/">http://www.serpentine.com/~bos/threads-faq/</A>.<P> + +There are also some online tutorials. Follow the links from the +LinuxThreads web page: +<A HREF="http://pauillac.inria.fr/~xleroy/linuxthreads">http://pauillac.inria.fr/~xleroy/linuxthreads</A>.<P> + +<H4><A NAME="B.2">B.2: I'd like to be informed of future developments on +LinuxThreads. Is there a mailing list for this purpose?</A></H4> + +I post LinuxThreads-related announcements on the newsgroup +<A HREF="news:comp.os.linux.announce">comp.os.linux.announce</A>, +and also on the mailing list +<code>linux-threads@magenet.com</code>. +You can subscribe to the latter by writing +<A HREF="mailto:majordomo@magenet.com">majordomo@magenet.com</A>.<P> + +<H4><A NAME="B.3">B.3: What are good places for discussing +LinuxThreads?</A></H4> + +For questions about programming with POSIX threads in general, use +the newsgroup +<A HREF="news:comp.programming.threads">comp.programming.threads</A>. +Be sure you read the +<A HREF="http://www.serpentine.com/~bos/threads-faq/">FAQ</A> +for this group before you post.<P> + +For Linux-specific questions, use +<A +HREF="news:comp.os.linux.development.apps">comp.os.linux.development.apps</A> +and <A +HREF="news:comp.os.linux.development.kernel">comp.os.linux.development.kernel</A>. +The latter is especially appropriate for questions relative to the +interface between the kernel and LinuxThreads.<P> + +<H4><A NAME="B.4">B.4: How should I report a possible bug in +LinuxThreads?</A></H4> + +If you're using glibc 2, the best way by far is to use the +<code>glibcbug</code> script to mail a bug report to the glibc +maintainers. <P> + +If you're using an older libc, or don't have the <code>glibcbug</code> +script on your machine, then e-mail me directly +(<code>Xavier.Leroy@inria.fr</code>).  <P> + +In both cases, before sending the bug report, make sure that it is not  +addressed already in this FAQ.  Also, try to send a short program that +reproduces the weird behavior you observed. <P> + +<H4><A NAME="B.5">B.5: I'd like to read the POSIX 1003.1c standard. Is +it available online?</A></H4> + +Unfortunately, no.  POSIX standards are copyrighted by IEEE, and +IEEE does not distribute them freely.  You can buy paper copies from +IEEE, but the price is fairly high ($120 or so). If you disagree with +this policy and you're an IEEE member, be sure to let them know.<P> + +On the other hand, you probably don't want to read the standard.  It's +very hard to read, written in standard-ese, and targeted to +implementors who already know threads inside-out.  A good book on +POSIX threads provides the same information in a much more readable form. +I can personally recommend Dave Butenhof's book, <CITE>Programming +with POSIX threads</CITE> (Addison-Wesley). Butenhof was part of the +POSIX committee and also designed the Digital Unix implementations of +POSIX threads, and it shows.<P> + +Another good source of information is the X/Open Group Single Unix +specification which is available both +<A HREF="http://www.rdg.opengroup.org/onlinepubs/7908799/index.html">on-line</A> +and as a +<A HREF="http://www.UNIX-systems.org/gosolo2/">book and CD/ROM</A>. +That specification includes pretty much all the POSIX standards, +including 1003.1c, with some extensions and clarifications.<P> + +<HR> +<P> + +<H2><A NAME="C">C.  Issues related to the C library</A></H2> + +<H4><A NAME="C.1">C.1: Which version of the C library should I use +with LinuxThreads?</A></H4> + +The best choice by far is glibc 2, a.k.a. libc 6.  It offers very good +support for multi-threading, and LinuxThreads has been closely +integrated with glibc 2.  The glibc 2 distribution contains the +sources of a specially adapted version of LinuxThreads.<P> + +glibc 2 comes preinstalled as the default C library on several Linux +distributions, such as RedHat 5 and up, and Debian 2. +Those distributions include the version of LinuxThreads matching +glibc 2.<P> + +<H4><A NAME="C.2">C.2: My system has libc 5 preinstalled, not glibc +2.  Can I still use LinuxThreads?</H4> + +Yes, but you're likely to run into some problems, as libc 5 only +offers minimal support for threads and contains some bugs that affect +multithreaded programs. <P> + +The versions of libc 5 that work best with LinuxThreads are +libc 5.2.18 on the one hand, and libc 5.4.12 or later on the other hand. +Avoid 5.3.12 and 5.4.7: these have problems with the per-thread errno +variable. <P> + +<H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a +recent libc 5?</A></H4> + +I'd recommend you switch to glibc 2.  Even for single-threaded +programs, glibc 2 is more solid and more standard-conformant than libc +5.  And the shortcomings of libc 5 almost preclude any serious +multi-threaded programming.<P> + +Switching an already installed +system from libc 5 to glibc 2 is not completely straightforward. +See the <A HREF="http://sunsite.unc.edu/LDP/HOWTO/Glibc2-HOWTO.html">Glibc2 +HOWTO</A> for more information.  Much easier is (re-)installing a +Linux distribution based on glibc 2, such as RedHat 6.<P> + +<H4><A NAME="C.4">C.4: Where can I find glibc 2 and the version of +LinuxThreads that goes with it?</A></H4> + +On <code>prep.ai.mit.edu</code> and its many, many mirrors around the world. +See <A +HREF="http://www.gnu.org/order/ftp.html">http://www.gnu.org/order/ftp.html</A> +for a list of mirrors.<P> + +<H4><A NAME="C.5">C.5: Where can I find libc 5 and the version of +LinuxThreads that goes with it?</A></H4> + +For libc 5, see <A HREF="ftp://sunsite.unc.edu/pub/Linux/devel/GCC/"><code>ftp://sunsite.unc.edu/pub/Linux/devel/GCC/</code></A>.<P> + +For the libc 5 version of LinuxThreads, see +<A HREF="ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/">ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/</A>.<P> + +<H4><A NAME="C.6">C.6: How can I recompile the glibc 2 version of the +LinuxThreads sources?</A></H4> + +You must transfer the whole glibc sources, then drop the LinuxThreads +sources in the <code>linuxthreads/</code> subdirectory, then recompile +glibc as a whole.  There are now too many inter-dependencies between +LinuxThreads and glibc 2 to allow separate re-compilation of LinuxThreads. +<P> + +<H4><A NAME="C.7">C.7: What is the correspondence between LinuxThreads  +version numbers, libc version numbers, and RedHat version +numbers?</A></H4> + +Here is a summary. (Information on Linux distributions other than +RedHat are welcome.)<P> + +<TABLE> +<TR><TD>LinuxThreads </TD> <TD>C library</TD> <TD>RedHat</TD></TR> +<TR><TD>0.7, 0.71 (for libc 5)</TD> <TD>libc 5.x</TD> <TD>RH 4.2</TD></TR> +<TR><TD>0.7, 0.71 (for glibc 2)</TD> <TD>glibc 2.0.x</TD> <TD>RH 5.x</TD></TR> +<TR><TD>0.8</TD> <TD>glibc 2.1.1</TD> <TD>RH 6.0</TD></TR> +<TR><TD>0.8</TD> <TD>glibc 2.1.2</TD> <TD>not yet released</TD></TR> +</TABLE> +<P> + +<HR> +<P> + +<H2><A NAME="D">D. Problems, weird behaviors, potential bugs</A></H2> + +<H4><A NAME="D.1">D.1: When I compile LinuxThreads, I run into problems in +file <code>libc_r/dirent.c</code></A></H4> + +You probably mean: +<PRE> +        libc_r/dirent.c:94: structure has no member named `dd_lock' +</PRE> +I haven't actually seen this problem, but several users reported it. +My understanding is that something is wrong in the include files of +your Linux installation (<code>/usr/include/*</code>). Make sure +you're using a supported version of the libc 5 library. (See question <A +HREF="#C.2">C.2</A>).<P> + +<H4><A NAME="D.2">D.2: When I compile LinuxThreads, I run into problems with +<CODE>/usr/include/sched.h</CODE>: there are several occurrences of +<CODE>_p</CODE> that the C compiler does not understand</A></H4> + +Yes, <CODE>/usr/include/sched.h</CODE> that comes with libc 5.3.12 is broken. +Replace it with the <code>sched.h</code> file contained in the +LinuxThreads distribution.  But really you should not be using libc +5.3.12 with LinuxThreads! (See question <A HREF="#C.2">C.1</A>.)<P> + +<H4><A NAME="D.3">D.3: My program does <CODE>fdopen()</CODE> on a file +descriptor opened on a pipe.  When I link it with LinuxThreads, +<CODE>fdopen()</CODE> always returns NULL!</A></H4> + +You're using one of the buggy versions of libc (5.3.12, 5.4.7., etc). +See question <A HREF="#C.1">C.1</A> above.<P> + +<H4><A NAME="D.4">D.4: My program creates a lot of threads, and after +a while <CODE>pthread_create()</CODE> no longer returns!</A></H4> + +This is known bug in the version of LinuxThreads that comes with glibc +2.1.1.  An upgrade to 2.1.2 is recommended. <P> + +<H4><A NAME="D.5">D.5: When I'm running a program that creates N +threads, <code>top</code> or <code>ps</code> +display N+2 processes that are running my program. What do all these +processes correspond to?</A></H4> + +Due to the general "one process per thread" model, there's one process +for the initial thread and N processes for the threads it created +using <CODE>pthread_create</CODE>.  That leaves one process +unaccounted for.  That extra process corresponds to the "thread +manager" thread, a thread created internally by LinuxThreads to handle +thread creation and thread termination.  This extra thread is asleep +most of the time. + +<H4><A NAME="D.6">D.6: Scheduling seems to be very unfair when there +is strong contention on a mutex: instead of giving the mutex to each +thread in turn, it seems that it's almost always the same thread that +gets the mutex. Isn't this completely broken behavior?</A></H4> + +That behavior has mostly disappeared in recent releases of +LinuxThreads (version 0.8 and up).  It was fairly common in older +releases, though. + +What happens in LinuxThreads 0.7 and before is the following: when a +thread unlocks a mutex, all other threads that were waiting on the +mutex are sent a signal which makes them runnable.  However, the +kernel scheduler may or may not restart them immediately.  If the +thread that unlocked the mutex tries to lock it again immediately +afterwards, it is likely that it will succeed, because the threads +haven't yet restarted.  This results in an apparently very unfair +behavior, when the same thread repeatedly locks and unlocks the mutex, +while other threads can't lock the mutex.<P> + +In LinuxThreads 0.8 and up, <code>pthread_unlock</code> restarts only +one waiting thread, and pre-assign the mutex to that thread.  Hence, +if the thread that unlocked the mutex tries to lock it again +immediately, it will block until other waiting threads have had a +chance to lock and unlock the mutex.  This results in much fairer +scheduling.<P> + +Notice however that even the old "unfair" behavior is perfectly +acceptable with respect to the POSIX standard: for the default +scheduling policy, POSIX makes no guarantees of fairness, such as "the +thread waiting for the mutex for the longest time always acquires it +first".  Properly written multithreaded code avoids that kind of heavy +contention on mutexes, and does not run into fairness problems.  If +you need scheduling guarantees, you should consider using the +real-time scheduling policies <code>SCHED_RR</code> and +<code>SCHED_FIFO</code>, which have precisely defined scheduling +behaviors. <P> + +<H4><A NAME="D.7">D.7: I have a simple test program with two threads +that do nothing but <CODE>printf()</CODE> in tight loops, and from the +printout it seems that only one thread is running, the other doesn't +print anything!</A></H4> + +Again, this behavior is characteristic of old releases of LinuxThreads +(0.7 and before); more recent versions (0.8 and up) should not exhibit +this behavior.<P> + +The reason for this behavior is explained in +question <A HREF="#D.6">D.6</A> above: <CODE>printf()</CODE> performs +locking on <CODE>stdout</CODE>, and thus your two threads contend very +heavily for the mutex associated with <CODE>stdout</CODE>.  But if you +do some real work between two calls to <CODE>printf()</CODE>, you'll +see that scheduling becomes much smoother.<P> + +<H4><A NAME="D.8">D.8: I've looked at <code><pthread.h></code> +and there seems to be a gross error in the <code>pthread_cleanup_push</code> +macro: it opens a block with <code>{</code> but does not close it! +Surely you forgot a <code>}</code> at the end of the macro, right? +</A></H4> + +Nope.  That's the way it should be.  The closing brace is provided by +the <code>pthread_cleanup_pop</code> macro.  The POSIX standard +requires <code>pthread_cleanup_push</code> and +<code>pthread_cleanup_pop</code> to be used in matching pairs, at the +same level of brace nesting.  This allows +<code>pthread_cleanup_push</code> to open a block in order to +stack-allocate some data structure, and +<code>pthread_cleanup_pop</code> to close that block.  It's ugly, but +it's the standard way of implementing cleanup handlers.<P> + +<H4><A NAME="D.9">D.9: I tried to use real-time threads and my program +loops like crazy and freezes the whole machine!</A></H4> + +Versions of LinuxThreads prior to 0.8 are susceptible to ``livelocks'' +(one thread loops, consuming 100% of the CPU time) in conjunction with +real-time scheduling.  Since real-time threads and processes have +higher priority than normal Linux processes, all other processes on +the machine, including the shell, the X server, etc, cannot run and +the machine appears frozen.<P> + +The problem is fixed in LinuxThreads 0.8.<P> + +<H4><A NAME="D.10">D.10: My application needs to create thousands of +threads, or maybe even more.  Can I do this with +LinuxThreads?</A></H4> + +No.  You're going to run into several hard limits: +<UL> +<LI>Each thread, from the kernel's standpoint, is one process.  Stock +Linux kernels are limited to at most 512 processes for the super-user, +and half this number for regular users.  This can be changed by +changing <code>NR_TASKS</code> in <code>include/linux/tasks.h</code> +and recompiling the kernel.  On the x86 processors at least, +architectural constraints seem to limit <code>NR_TASKS</code> to 4090 +at most. +<LI>LinuxThreads contains a table of all active threads.  This table +has room for 1024 threads at most.  To increase this limit, you must +change <code>PTHREAD_THREADS_MAX</code> in the LinuxThreads sources +and recompile. +<LI>By default, each thread reserves 2M of virtual memory space for +its stack.  This space is just reserved; actual memory is allocated +for the stack on demand.  But still, on a 32-bit processor, the total +virtual memory space available for the stacks is on the order of 1G, +meaning that more than 500 threads will have a hard time fitting in. +You can overcome this limitation by moving to a 64-bit platform, or by +allocating smaller stacks yourself using the <code>setstackaddr</code> +attribute. +<LI>Finally, the Linux kernel contains many algorithms that run in +time proportional to the number of process table entries.  Increasing +this number drastically will slow down the kernel operations +noticeably. +</UL> +(Other POSIX threads libraries have similar limitations, by the way.) +For all those reasons, you'd better restructure your application so +that it doesn't need more than, say, 100 threads.  For instance, +in the case of a multithreaded server, instead of creating a new +thread for each connection, maintain a fixed-size pool of worker +threads that pick incoming connection requests from a queue.<P> + +<HR> +<P> + +<H2><A NAME="E">E. Missing functions, wrong types, etc</A></H2> + +<H4><A NAME="E.1">E.1: Where is <CODE>pthread_yield()</CODE> ? How +comes LinuxThreads does not implement it?</A></H4> + +Because it's not part of the (final) POSIX 1003.1c standard. +Several drafts of the standard contained <CODE>pthread_yield()</CODE>, +but then the POSIX guys discovered it was redundant with +<CODE>sched_yield()</CODE> and dropped it.  So, just use +<CODE>sched_yield()</CODE> instead. + +<H4><A NAME="E.2">E.2: I've found some type errors in +<code><pthread.h></code>. +For instance, the second argument to <CODE>pthread_create()</CODE> +should be a <CODE>pthread_attr_t</CODE>, not a +<CODE>pthread_attr_t *</CODE>. Also, didn't you forget to declare +<CODE>pthread_attr_default</CODE>?</A></H4> + +No, I didn't.  What you're describing is draft 4 of the POSIX +standard, which is used in OSF DCE threads.  LinuxThreads conforms to the +final standard.  Even though the functions have the same names as in +draft 4 and DCE, their calling conventions are slightly different.  In +particular, attributes are passed by reference, not by value, and +default attributes are denoted by the NULL pointer.  Since draft 4/DCE +will eventually disappear, you'd better port your program to use the +standard interface.<P> + +<H4><A NAME="E.3">E.3: I'm porting an application from Solaris and I +have to rename all thread functions from <code>thr_blah</code> to +<CODE>pthread_blah</CODE>.  This is very annoying.  Why did you change +all the function names?</A></H4> + +POSIX did it.  The <code>thr_*</code> functions correspond to Solaris +threads, an older thread interface that you'll find only under +Solaris.  The <CODE>pthread_*</CODE> functions correspond to POSIX +threads, an international standard available for many, many platforms. +Even Solaris 2.5 and later support the POSIX threads interface.  So, +do yourself a favor and rewrite your code to use POSIX threads: this +way, it will run unchanged under Linux, Solaris, and quite a lot of +other platforms.<P> + +<H4><A NAME="E.4">E.4: How can I suspend and resume a thread from +another thread? Solaris has the <CODE>thr_suspend()</CODE> and +<CODE>thr_resume()</CODE> functions to do that; why don't you?</A></H4> + +The POSIX standard provides <B>no</B> mechanism by which a thread A can +suspend the execution of another thread B, without cooperation from B. +The only way to implement a suspend/restart mechanism is to have B +check periodically some global variable for a suspend request +and then suspend itself on a condition variable, which another thread +can signal later to restart B.<P> + +Notice that <CODE>thr_suspend()</CODE> is inherently dangerous and +prone to race conditions.  For one thing, there is no control on where +the target thread stops: it can very well be stopped in the middle of +a critical section, while holding mutexes.  Also, there is no +guarantee on when the target thread will actually stop.  For these +reasons, you'd be much better off using mutexes and conditions +instead.  The only situations that really require the ability to +suspend a thread are debuggers and some kind of garbage collectors.<P> + +If you really must suspend a thread in LinuxThreads, you can send it a +<CODE>SIGSTOP</CODE> signal with <CODE>pthread_kill</CODE>. Send +<CODE>SIGCONT</CODE> for restarting it. +Beware, this is specific to LinuxThreads and entirely non-portable. +Indeed, a truly conforming POSIX threads implementation will stop all +threads when one thread receives the <CODE>SIGSTOP</CODE> signal! +One day, LinuxThreads will implement that behavior, and the +non-portable hack with <CODE>SIGSTOP</CODE> won't work anymore.<P> + +<H4><A NAME="E.5">E.5: Does LinuxThreads implement +<CODE>pthread_attr_setstacksize()</CODE> and +<CODE>pthread_attr_setstackaddr()</CODE>?</A></H4> + +These optional functions are provided in recent versions of +LinuxThreads (0.8 and up).  Earlier releases did not provide these +optional components of the POSIX standard.<P> + +Even if <CODE>pthread_attr_setstacksize()</CODE> and +<CODE>pthread_attr_setstackaddr()</CODE> are now provided, we still +recommend that you do not use them unless you really have strong +reasons for doing so.  The default stack allocation strategy for +LinuxThreads is nearly optimal: stacks start small (4k) and +automatically grow on demand to a fairly large limit (2M). +Moreover, there is no portable way to estimate the stack requirements +of a thread, so setting the stack size yourself makes your program +less reliable and non-portable.<P> + +<H4><A NAME="E.6">E.6: LinuxThreads does not support the +<CODE>PTHREAD_SCOPE_PROCESS</CODE> value of the "contentionscope" +attribute.  Why? </A></H4> + +With a "one-to-one" model, as in LinuxThreads (one kernel execution +context per thread), there is only one scheduler for all processes and +all threads on the system.  So, there is no way to obtain the behavior of +<CODE>PTHREAD_SCOPE_PROCESS</CODE>. + +<H4><A NAME="E.7">E.7: LinuxThreads does not implement process-shared +mutexes, conditions, and semaphores. Why?</A></H4> + +This is another optional component of the POSIX standard.  Portable +applications should test <CODE>_POSIX_THREAD_PROCESS_SHARED</CODE> +before using this facility. +<P> +The goal of this extension is to allow different processes (with +different address spaces) to synchronize through mutexes, conditions +or semaphores allocated in shared memory (either SVR4 shared memory +segments or <CODE>mmap()</CODE>ed files). +<P> +The reason why this does not work in LinuxThreads is that mutexes, +conditions, and semaphores are not self-contained: their waiting +queues contain pointers to linked lists of thread descriptors, and +these pointers are meaningful only in one address space. +<P> +Matt Messier and I spent a significant amount of time trying to design a +suitable mechanism for sharing waiting queues between processes.  We +came up with several solutions that combined two of the following +three desirable features, but none that combines all three: +<UL> +<LI>allow sharing between processes having different UIDs +<LI>supports cancellation +<LI>supports <CODE>pthread_cond_timedwait</CODE> +</UL> +We concluded that kernel support is required to share mutexes, +conditions and semaphores between processes.  That's one place where +Linus Torvalds's intuition that "all we need in the kernel is +<CODE>clone()</CODE>" fails. +<P> +Until suitable kernel support is available, you'd better use +traditional interprocess communications to synchronize different +processes: System V semaphores and message queues, or pipes, or sockets. +<P> + +<HR> +<P> + +<H2><A NAME="F">F. C++ issues</A></H2> + +<H4><A NAME="F.1">F.1: Are there C++ wrappers for LinuxThreads?</A></H4> + +Douglas Schmidt's ACE library contains, among a lot of other +things, C++ wrappers for LinuxThreads and quite a number of other +thread libraries.  Check out +<A HREF="http://www.cs.wustl.edu/~schmidt/ACE.html">http://www.cs.wustl.edu/~schmidt/ACE.html</A><P> + +<H4><A NAME="F.2">F.2: I'm trying to use LinuxThreads from a C++ +program, and the compiler complains about the third argument to +<CODE>pthread_create()</CODE> !</A></H4> + +You're probably trying to pass a class member function or some +other C++ thing as third argument to <CODE>pthread_create()</CODE>. +Recall that <CODE>pthread_create()</CODE> is a C function, and it must +be passed a C function as third argument.<P> + +<H4><A NAME="F.3">F.3: I'm trying to use LinuxThreads in conjunction +with libg++, and I'm having all sorts of trouble.</A></H4> + +>From what I understand, thread support in libg++ is completely broken, +especially with respect to locking of iostreams.  H.J.Lu wrote: +<BLOCKQUOTE> +If you want to use thread, I can only suggest egcs and glibc. You +can find egcs at +<A HREF="http://www.cygnus.com/egcs">http://www.cygnus.com/egcs</A>. +egcs has libsdtc++, which is MT safe under glibc 2. If you really +want to use the libg++, I have a libg++ add-on for egcs. +</BLOCKQUOTE> +<HR> +<P> + +<H2><A NAME="G">G. Debugging LinuxThreads programs</A></H2> + +<H4><A NAME="G.1">G.1: Can I debug LinuxThreads program using gdb?</A></H4> + +Yes, but not with the stock gdb 4.17.  You need a specially patched +version of gdb 4.17 developed by Eric Paire and colleages at The Open +Group, Grenoble.  The patches against gdb 4.17 are available at +<A HREF="http://www.gr.opengroup.org/java/jdk/linux/debug.htm"><code>http://www.gr.opengroup.org/java/jdk/linux/debug.htm</code></A>. +Precompiled binaries of the patched gdb are available in RedHat's RPM +format at <A +HREF="http://odin.appliedtheory.com/"><code>http://odin.appliedtheory.com/</code></A>.<P> + +Some Linux distributions provide an already-patched version of gdb; +others don't.  For instance, the gdb in RedHat 5.2 is thread-aware, +but apparently not the one in RedHat 6.0.  Just ask (politely) the +makers of your Linux distributions to please make sure that they apply +the correct patches to gdb.<P> + +<H4><A NAME="G.2">G.2: Does it work with post-mortem debugging?</A></H4> + +Not very well.  Generally, the core file does not correspond to the +thread that crashed.  The reason is that the kernel will not dump core +for a process that shares its memory with other processes, such as the +other threads of your program.  So, the thread that crashes silently +disappears without generating a core file.  Then, all other threads of +your program die on the same signal that killed the crashing thread. +(This is required behavior according to the POSIX standard.)  The last +one that dies is no longer sharing its memory with anyone else, so the +kernel generates a core file for that thread.  Unfortunately, that's +not the thread you are interested in. + +<H4><A NAME="G.3">G.3: Any other ways to debug multithreaded programs, then?</A></H4> + +Assertions and <CODE>printf()</CODE> are your best friends.  Try to debug +sequential parts in a single-threaded program first.  Then, put +<CODE>printf()</CODE> statements all over the place to get execution traces. +Also, check invariants often with the <CODE>assert()</CODE> macro.  In truth, +there is no other effective way (save for a full formal proof of your +program) to track down concurrency bugs.  Debuggers are not really +effective for subtle concurrency problems, because they disrupt +program execution too much.<P> + +<HR> +<P> + +<H2><A NAME="H">H. Compiling multithreaded code; errno madness</A></H2> + +<H4><A NAME="H.1">H.1: You say all multithreaded code must be compiled +with <CODE>_REENTRANT</CODE> defined. What difference does it make?</A></H4> + +It affects include files in three ways: +<UL> +<LI> The include files define prototypes for the reentrant variants of +some of the standard library functions, +e.g. <CODE>gethostbyname_r()</CODE> as a reentrant equivalent to +<CODE>gethostbyname()</CODE>.<P> + +<LI> If <CODE>_REENTRANT</CODE> is defined, some +<code><stdio.h></code> functions are no longer defined as macros, +e.g. <CODE>getc()</CODE> and <CODE>putc()</CODE>. In a multithreaded +program, stdio functions require additional locking, which the macros +don't perform, so we must call functions instead.<P> + +<LI> More importantly, <code><errno.h></code> redefines errno when +<CODE>_REENTRANT</CODE> is +defined, so that errno refers to the thread-specific errno location +rather than the global errno variable.  This is achieved by the +following <code>#define</code> in <code><errno.h></code>: +<PRE> +        #define errno (*(__errno_location())) +</PRE> +which causes each reference to errno to call the +<CODE>__errno_location()</CODE> function for obtaining the location +where error codes are stored.  libc provides a default definition of +<CODE>__errno_location()</CODE> that always returns +<code>&errno</code> (the address of the global errno variable). Thus, +for programs not linked with LinuxThreads, defining +<CODE>_REENTRANT</CODE> makes no difference w.r.t. errno processing. +But LinuxThreads redefines <CODE>__errno_location()</CODE> to return a +location in the thread descriptor reserved for holding the current +value of errno for the calling thread.  Thus, each thread operates on +a different errno location. +</UL> +<P> + +<H4><A NAME="H.2">H.2: Why is it so important that each thread has its +own errno variable? </A></H4> + +If all threads were to store error codes in the same, global errno +variable, then the value of errno after a system call or library +function returns would be unpredictable:  between the time a system +call stores its error code in the global errno and your code inspects +errno to see which error occurred, another thread might have stored +another error code in the same errno location. <P> + +<H4><A NAME="H.3">H.3: What happens if I link LinuxThreads with code +not compiled with <CODE>-D_REENTRANT</CODE>?</A></H4> + +Lots of trouble.  If the code uses <CODE>getc()</CODE> or +<CODE>putc()</CODE>, it will perform I/O without proper interlocking +of the stdio buffers; this can cause lost output, duplicate output, or +just crash other stdio functions.  If the code consults errno, it will +get back the wrong error code.  The following code fragment is a +typical example: +<PRE> +        do { +          r = read(fd, buf, n); +          if (r == -1) { +            if (errno == EINTR)   /* an error we can handle */ +              continue; +            else {                /* other errors are fatal */ +              perror("read failed"); +              exit(100); +            } +          } +        } while (...); +</PRE> +Assume this code is not compiled with <CODE>-D_REENTRANT</CODE>, and +linked with LinuxThreads.  At run-time, <CODE>read()</CODE> is +interrupted.  Since the C library was compiled with +<CODE>-D_REENTRANT</CODE>, <CODE>read()</CODE> stores its error code +in the location pointed to by <CODE>__errno_location()</CODE>, which +is the thread-local errno variable.  Then, the code above sees that +<CODE>read()</CODE> returns -1 and looks up errno.  Since +<CODE>_REENTRANT</CODE> is not defined, the reference to errno +accesses the global errno variable, which is most likely 0.  Hence the +code concludes that it cannot handle the error and stops.<P> + +<H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals +<code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4> + +The short answer is: because the Linux kernel you're using does not +support realtime signals.  <P> + +LinuxThreads needs two signals for its internal operation. +One is used to suspend and restart threads blocked on mutex, condition +or semaphore operations.  The other is used for thread +cancellation.<P> + +On ``old'' kernels (2.0 and early 2.1 kernels), there are only 32 +signals available and the kernel reserves all of them but two: +<code>SIGUSR1</code> and <code>SIGUSR2</code>.  So, LinuxThreads has +no choice but use those two signals.<P> + +On recent kernels (2.2 and up), more than 32 signals are provided in +the form of realtime signals. When run on one of those kernels, +LinuxThreads uses two reserved realtime signals for its internal +operation, thus leaving <code>SIGUSR1</code> and <code>SIGUSR2</code> +free for user code.  (This works only with glibc, not with libc 5.) <P> + +<H4><A NAME="H.5">H.5: Is the stack of one thread visible from the +other threads?  Can I pass a pointer into my stack to other threads? +</A></H4> + +Yes, you can -- if you're very careful.  The stacks are indeed visible +from all threads in the system.  Some non-POSIX thread libraries seem +to map the stacks for all threads at the same virtual addresses and +change the memory mapping when they switch from one thread to +another.  But this is not the case for LinuxThreads, as it would make +context switching between threads more expensive, and at any rate +might not conform to the POSIX standard.<P> + +So, you can take the address of an "auto" variable and pass it to +other threads via shared data structures.  However, you need to make +absolutely sure that the function doing this will not return as long +as other threads need to access this address.  It's the usual mistake +of returning the address of an "auto" variable, only made much worse +because of concurrency.  It's much, much safer to systematically +heap-allocate all shared data structures. <P> + +<HR> +<P> + +<H2><A NAME="I">I.  X-Windows and other libraries</A></H2> + +<H4><A NAME="I.1">I.1: My program uses both Xlib and LinuxThreads. +It stops very early with an "Xlib: unknown 0 error" message.  What +does this mean? </A></H4> + +That's a prime example of the errno problem described in question <A +HREF="#H.2">H.2</A>.  The binaries for Xlib you're using have not been +compiled with <CODE>-D_REENTRANT</CODE>.  It happens Xlib contains a +piece of code very much like the one in question <A +HREF="#H.2">H.2</A>.  So, your Xlib fetches the error code from the +wrong errno location and concludes that an error it cannot handle +occurred.<P> + +<H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded X +Windows client? </A></H4> + +The best solution is to use X libraries that have been compiled with +multithreading options set.  Linux distributions that come with glibc +2 as the main C library generally provide thread-safe X libraries. +At least, that seems to be the case for RedHat 5 and later.<P> + +You can try to recompile yourself the X libraries with multithreading +options set.  They contain optional support for multithreading; it's +just that the binaries provided by your Linux distribution were built +without this support.  See the file <code>README.Xfree3.3</code> in +the LinuxThreads distribution for patches and info on how to compile +thread-safe X libraries from the Xfree3.3 distribution.  The Xfree3.3 +sources are readily available in most Linux distributions, e.g. as a +source RPM for RedHat.  Be warned, however, that X Windows is a huge +system, and recompiling even just the libraries takes a lot of time +and disk space.<P> + +Another, less involving solution is to call X functions only from the +main thread of your program.  Even if all threads have their own errno +location, the main thread uses the global errno variable for its errno +location.  Thus, code not compiled with <code>-D_REENTRANT</code> +still "sees" the right error values if it executes in the main thread +only. <P> + +<H4><A NAME="I.2">This is a lot of work. Don't you have precompiled +thread-safe X libraries that you could distribute?</A></H4> + +No, I don't.  Sorry.  But consider installing a Linux distribution +that comes with thread-safe X libraries, such as RedHat 6.<P> + +<H4><A NAME="I.3">I.3: Can I use library FOO in a multithreaded +program?</A></H4> + +Most libraries cannot be used "as is" in a multithreaded program. +For one thing, they are not necessarily thread-safe: calling +simultaneously two functions of the library from two threads might not +work, due to internal use of global variables and the like.  Second, +the libraries must have been compiled with <CODE>-D_REENTRANT</CODE> to avoid +the errno problems explained in question <A HREF="#H.2">H.2</A>. +<P> + +<H4><A NAME="I.4">I.4: What if I make sure that only one thread calls +functions in these libraries?</A></H4> + +This avoids problems with the library not being thread-safe.  But +you're still vulnerable to errno problems.  At the very least, a +recompile of the library with <CODE>-D_REENTRANT</CODE> is needed. +<P> + +<H4><A NAME="I.5">I.5: What if I make sure that only the main thread +calls functions in these libraries?</A></H4> + +That might actually work.  As explained in question <A HREF="#I.1">I.1</A>, +the main thread uses the global errno variable, and can therefore +execute code not compiled with <CODE>-D_REENTRANT</CODE>.<P> + +<H4><A NAME="I.6">I.6: SVGAlib doesn't work with LinuxThreads.  Why? +</A></H4> + +Because both LinuxThreads and SVGAlib use the signals +<code>SIGUSR1</code> and <code>SIGUSR2</code>.  See question <A +HREF="#H.4">H.4</A>. +<P> + + +<HR> +<P> + +<H2><A NAME="J">J.  Signals and threads</A></H2> + +<H4><A NAME="J.1">J.1: When it comes to signals, what is shared +between threads and what isn't?</A></H4> + +Signal handlers are shared between all threads: when a thread calls +<CODE>sigaction()</CODE>, it sets how the signal is handled not only +for itself, but for all other threads in the program as well.<P> + +On the other hand, signal masks are per-thread: each thread chooses +which signals it blocks independently of others.  At thread creation +time, the newly created thread inherits the signal mask of the thread +calling <CODE>pthread_create()</CODE>.  But afterwards, the new thread +can modify its signal mask independently of its creator thread.<P> + +<H4><A NAME="J.2">J.2: When I send a <CODE>SIGKILL</CODE> to a +particular thread using <CODE>pthread_kill</CODE>, all my threads are +killed!</A></H4> + +That's how it should be.  The POSIX standard mandates that all threads +should terminate when the process (i.e. the collection of all threads +running the program) receives a signal whose effect is to +terminate the process (such as <CODE>SIGKILL</CODE> or <CODE>SIGINT</CODE> +when no handler is installed on that signal).  This behavior makes a +lot of sense: when you type "ctrl-C" at the keyboard, or when a thread +crashes on a division by zero or a segmentation fault, you really want +all threads to stop immediately, not just the one that caused the +segmentation violation or that got the <CODE>SIGINT</CODE> signal. +(This assumes default behavior for those signals; see question +<A HREF="#J.3">J.3</A> if you install handlers for those signals.)<P> + +If you're trying to terminate a thread without bringing the whole +process down, use <code>pthread_cancel()</code>.<P> + +<H4><A NAME="J.3">J.3: I've installed a handler on a signal.  Which +thread executes the handler when the signal is received?</A></H4> + +If the signal is generated by a thread during its execution (e.g. a +thread executes a division by zero and thus generates a +<CODE>SIGFPE</CODE> signal), then the handler is executed by that +thread.  This also applies to signals generated by +<CODE>raise()</CODE>.<P> + +If the signal is sent to a particular thread using +<CODE>pthread_kill()</CODE>, then that thread executes the handler.<P> + +If the signal is sent via <CODE>kill()</CODE> or the tty interface +(e.g. by pressing ctrl-C), then the POSIX specs say that the handler +is executed by any thread in the process that does not currently block +the signal.  In other terms, POSIX considers that the signal is sent +to the process (the collection of all threads) as a whole, and any +thread that is not blocking this signal can then handle it.<P> + +The latter case is where LinuxThreads departs from the POSIX specs. +In LinuxThreads, there is no real notion of ``the process as a whole'': +in the kernel, each thread is really a distinct process with a +distinct PID, and signals sent to the PID of a thread can only be +handled by that thread.  As long as no thread is blocking the signal, +the behavior conforms to the standard: one (unspecified) thread of the +program handles the signal.  But if the thread to which PID the signal +is sent blocks the signal, and some other thread does not block the +signal, then LinuxThreads will simply queue in +that thread and execute the handler only when that thread unblocks +the signal, instead of executing the handler immediately in the other +thread that does not block the signal.<P> + +This is to be viewed as a LinuxThreads bug, but I currently don't see +any way to implement the POSIX behavior without kernel support.<P> + +<H4><A NAME="J.3">J.3: How shall I go about mixing signals and threads +in my program? </A></H4> + +The less you mix them, the better.  Notice that all +<CODE>pthread_*</CODE> functions are not async-signal safe, meaning +that you should not call them from signal handlers.  This +recommendation is not to be taken lightly: your program can deadlock +if you call a <CODE>pthread_*</CODE> function from a signal handler! +<P> + +The only sensible things you can do from a signal handler is set a +global flag, or call <CODE>sem_post</CODE> on a semaphore, to record +the delivery of the signal.  The remainder of the program can then +either poll the global flag, or use <CODE>sem_wait()</CODE> and +<CODE>sem_trywait()</CODE> on the semaphore.<P> + +Another option is to do nothing in the signal handler, and dedicate +one thread (preferably the initial thread) to wait synchronously for +signals, using <CODE>sigwait()</CODE>, and send messages to the other +threads accordingly. + +<H4><A NAME="J.4">J.4: When one thread is blocked in +<CODE>sigwait()</CODE>, other threads no longer receive the signals +<CODE>sigwait()</CODE> is waiting for!  What happens? </A></H4> + +It's an unfortunate consequence of how LinuxThreads implements +<CODE>sigwait()</CODE>.  Basically, it installs signal handlers on all +signals waited for, in order to record which signal was received. +Since signal handlers are shared with the other threads, this +temporarily deactivates any signal handlers you might have previously +installed on these signals.<P> + +Though surprising, this behavior actually seems to conform to the +POSIX standard.  According to POSIX, <CODE>sigwait()</CODE> is +guaranteed to work as expected only if all other threads in the +program block the signals waited for (otherwise, the signals could be +delivered to other threads than the one doing <CODE>sigwait()</CODE>, +which would make <CODE>sigwait()</CODE> useless).  In this particular +case, the problem described in this question does not appear.<P> + +One day, <CODE>sigwait()</CODE> will be implemented in the kernel, +along with others POSIX 1003.1b extensions, and <CODE>sigwait()</CODE> +will have a more natural behavior (as well as better performances).<P> + +<HR> +<P> + +<H2><A NAME="K">K.  Internals of LinuxThreads</A></H2> + +<H4><A NAME="K.1">K.1: What is the implementation model for +LinuxThreads?</A></H4> + +LinuxThreads follows the so-called "one-to-one" model: each thread is +actually a separate process in the kernel.  The kernel scheduler takes +care of scheduling the threads, just like it schedules regular +processes.  The threads are created with the Linux +<code>clone()</code> system call, which is a generalization of +<code>fork()</code> allowing the new process to share the memory +space, file descriptors, and signal handlers of the parent.<P> + +Advantages of the "one-to-one" model include: +<UL> +<LI> minimal overhead on CPU-intensive multiprocessing (with +about one thread per processor); +<LI> minimal overhead on I/O operations; +<LI> a simple and robust implementation (the kernel scheduler does +most of the hard work for us). +</UL> +The main disadvantage is more expensive context switches on mutex and +condition operations, which must go through the kernel.  This is +mitigated by the fact that context switches in the Linux kernel are +pretty efficient.<P> + +<H4><A NAME="K.2">K.2: Have you considered other implementation +models?</A></H4> + +There are basically two other models.  The "many-to-one" model +relies on a user-level scheduler that context-switches between the +threads entirely in user code; viewed from the kernel, there is only +one process running.  This model is completely out of the question for +me, since it does not take advantage of multiprocessors, and require +unholy magic to handle blocking I/O operations properly.  There are +several user-level thread libraries available for Linux, but I found +all of them deficient in functionality, performance, and/or robustness. +<P> + +The "many-to-many" model combines both kernel-level and user-level +scheduling: several kernel-level threads run concurrently, each +executing a user-level scheduler that selects between user threads. +Most commercial Unix systems (Solaris, Digital Unix, IRIX) implement +POSIX threads this way.  This model combines the advantages of both +the "many-to-one" and the "one-to-one" model, and is attractive +because it avoids the worst-case behaviors of both models -- +especially on kernels where context switches are expensive, such as +Digital Unix.  Unfortunately, it is pretty complex to implement, and +requires kernel support which Linux does not provide.  Linus Torvalds +and other Linux kernel developers have always been pushing the +"one-to-one" model in the name of overall simplicity, and are doing a +pretty good job of making kernel-level context switches between +threads efficient.  LinuxThreads is just following the general +direction they set.<P> + +<HR> +<ADDRESS>Xavier.Leroy@inria.fr</ADDRESS> +</BODY> +</HTML> diff --git a/libpthread/linuxthreads/LICENSE b/libpthread/linuxthreads/LICENSE new file mode 100644 index 000000000..7bcca6050 --- /dev/null +++ b/libpthread/linuxthreads/LICENSE @@ -0,0 +1,501 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE +********************************** + +                         Version 2, June 1991 + +     Copyright (C) 1991 Free Software Foundation, Inc. +     59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA +      +     Everyone is permitted to copy and distribute verbatim copies +     of this license document, but changing it is not allowed. +      +     [This is the first released version of the library GPL.  It is +      numbered 2 because it goes with version 2 of the ordinary GPL.] + +Preamble +======== + +   The licenses for most software are designed to take away your +freedom to share and change it.  By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +   This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it.  You can use it for +your libraries, too. + +   When we speak of free software, we are referring to freedom, not +price.  Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it in +new free programs; and that you know you can do these things. + +   To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the library, or if you modify it. + +   For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you.  You must make sure that they, too, receive or can get the source +code.  If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it.  And you must show them these terms so they know their rights. + +   Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + +   Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library.  If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + +   Finally, any free program is threatened constantly by software +patents.  We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software.  To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +   Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License, which was designed for utility +programs.  This license, the GNU Library General Public License, +applies to certain designated libraries.  This license is quite +different from the ordinary one; be sure to read it in full, and don't +assume that anything in it is the same as in the ordinary license. + +   The reason we have a separate public license for some libraries is +that they blur the distinction we usually make between modifying or +adding to a program and simply using it.  Linking a program with a +library, without changing the library, is in some sense simply using +the library, and is analogous to running a utility program or +application program.  However, in a textual and legal sense, the linked +executable is a combined work, a derivative of the original library, +and the ordinary General Public License treats it as such. + +   Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries.  We +concluded that weaker conditions might promote sharing better. + +   However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves.  This Library General Public License is intended +to permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them.  (We have not seen how to +achieve this as regards changes in header files, but we have achieved +it as regards changes in the actual functions of the Library.)  The +hope is that this will lead to faster development of free libraries. + +   The precise terms and conditions for copying, distribution and +modification follow.  Pay close attention to the difference between a +"work based on the library" and a "work that uses the library".  The +former contains code derived from the library, while the latter only +works together with the library. + +   Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + +    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +  0. This License Agreement applies to any software library which +     contains a notice placed by the copyright holder or other +     authorized party saying it may be distributed under the terms of +     this Library General Public License (also called "this License"). +     Each licensee is addressed as "you". + +     A "library" means a collection of software functions and/or data +     prepared so as to be conveniently linked with application programs +     (which use some of those functions and data) to form executables. + +     The "Library", below, refers to any such software library or work +     which has been distributed under these terms.  A "work based on the +     Library" means either the Library or any derivative work under +     copyright law: that is to say, a work containing the Library or a +     portion of it, either verbatim or with modifications and/or +     translated straightforwardly into another language.  (Hereinafter, +     translation is included without limitation in the term +     "modification".) + +     "Source code" for a work means the preferred form of the work for +     making modifications to it.  For a library, complete source code +     means all the source code for all modules it contains, plus any +     associated interface definition files, plus the scripts used to +     control compilation and installation of the library. + +     Activities other than copying, distribution and modification are +     not covered by this License; they are outside its scope.  The act +     of running a program using the Library is not restricted, and +     output from such a program is covered only if its contents +     constitute a work based on the Library (independent of the use of +     the Library in a tool for writing it).  Whether that is true +     depends on what the Library does and what the program that uses +     the Library does. + +  1. You may copy and distribute verbatim copies of the Library's +     complete source code as you receive it, in any medium, provided +     that you conspicuously and appropriately publish on each copy an +     appropriate copyright notice and disclaimer of warranty; keep +     intact all the notices that refer to this License and to the +     absence of any warranty; and distribute a copy of this License +     along with the Library. + +     You may charge a fee for the physical act of transferring a copy, +     and you may at your option offer warranty protection in exchange +     for a fee. + +  2. You may modify your copy or copies of the Library or any portion +     of it, thus forming a work based on the Library, and copy and +     distribute such modifications or work under the terms of Section 1 +     above, provided that you also meet all of these conditions: + +       a. The modified work must itself be a software library. + +       b. You must cause the files modified to carry prominent notices +          stating that you changed the files and the date of any change. + +       c. You must cause the whole of the work to be licensed at no +          charge to all third parties under the terms of this License. + +       d. If a facility in the modified Library refers to a function or +          a table of data to be supplied by an application program that +          uses the facility, other than as an argument passed when the +          facility is invoked, then you must make a good faith effort +          to ensure that, in the event an application does not supply +          such function or table, the facility still operates, and +          performs whatever part of its purpose remains meaningful. + +          (For example, a function in a library to compute square roots +          has a purpose that is entirely well-defined independent of the +          application.  Therefore, Subsection 2d requires that any +          application-supplied function or table used by this function +          must be optional: if the application does not supply it, the +          square root function must still compute square roots.) + +     These requirements apply to the modified work as a whole.  If +     identifiable sections of that work are not derived from the +     Library, and can be reasonably considered independent and separate +     works in themselves, then this License, and its terms, do not +     apply to those sections when you distribute them as separate +     works.  But when you distribute the same sections as part of a +     whole which is a work based on the Library, the distribution of +     the whole must be on the terms of this License, whose permissions +     for other licensees extend to the entire whole, and thus to each +     and every part regardless of who wrote it. + +     Thus, it is not the intent of this section to claim rights or +     contest your rights to work written entirely by you; rather, the +     intent is to exercise the right to control the distribution of +     derivative or collective works based on the Library. + +     In addition, mere aggregation of another work not based on the +     Library with the Library (or with a work based on the Library) on +     a volume of a storage or distribution medium does not bring the +     other work under the scope of this License. + +  3. You may opt to apply the terms of the ordinary GNU General Public +     License instead of this License to a given copy of the Library. +     To do this, you must alter all the notices that refer to this +     License, so that they refer to the ordinary GNU General Public +     License, version 2, instead of to this License.  (If a newer +     version than version 2 of the ordinary GNU General Public License +     has appeared, then you can specify that version instead if you +     wish.)  Do not make any other change in these notices. + +     Once this change is made in a given copy, it is irreversible for +     that copy, so the ordinary GNU General Public License applies to +     all subsequent copies and derivative works made from that copy. + +     This option is useful when you wish to copy part of the code of +     the Library into a program that is not a library. + +  4. You may copy and distribute the Library (or a portion or +     derivative of it, under Section 2) in object code or executable +     form under the terms of Sections 1 and 2 above provided that you +     accompany it with the complete corresponding machine-readable +     source code, which must be distributed under the terms of Sections +     1 and 2 above on a medium customarily used for software +     interchange. + +     If distribution of object code is made by offering access to copy +     from a designated place, then offering equivalent access to copy +     the source code from the same place satisfies the requirement to +     distribute the source code, even though third parties are not +     compelled to copy the source along with the object code. + +  5. A program that contains no derivative of any portion of the +     Library, but is designed to work with the Library by being +     compiled or linked with it, is called a "work that uses the +     Library".  Such a work, in isolation, is not a derivative work of +     the Library, and therefore falls outside the scope of this License. + +     However, linking a "work that uses the Library" with the Library +     creates an executable that is a derivative of the Library (because +     it contains portions of the Library), rather than a "work that +     uses the library".  The executable is therefore covered by this +     License.  Section 6 states terms for distribution of such +     executables. + +     When a "work that uses the Library" uses material from a header +     file that is part of the Library, the object code for the work may +     be a derivative work of the Library even though the source code is +     not.  Whether this is true is especially significant if the work +     can be linked without the Library, or if the work is itself a +     library.  The threshold for this to be true is not precisely +     defined by law. + +     If such an object file uses only numerical parameters, data +     structure layouts and accessors, and small macros and small inline +     functions (ten lines or less in length), then the use of the object +     file is unrestricted, regardless of whether it is legally a +     derivative work.  (Executables containing this object code plus +     portions of the Library will still fall under Section 6.) + +     Otherwise, if the work is a derivative of the Library, you may +     distribute the object code for the work under the terms of Section +     6.  Any executables containing that work also fall under Section 6, +     whether or not they are linked directly with the Library itself. + +  6. As an exception to the Sections above, you may also compile or +     link a "work that uses the Library" with the Library to produce a +     work containing portions of the Library, and distribute that work +     under terms of your choice, provided that the terms permit +     modification of the work for the customer's own use and reverse +     engineering for debugging such modifications. + +     You must give prominent notice with each copy of the work that the +     Library is used in it and that the Library and its use are covered +     by this License.  You must supply a copy of this License.  If the +     work during execution displays copyright notices, you must include +     the copyright notice for the Library among them, as well as a +     reference directing the user to the copy of this License.  Also, +     you must do one of these things: + +       a. Accompany the work with the complete corresponding +          machine-readable source code for the Library including +          whatever changes were used in the work (which must be +          distributed under Sections 1 and 2 above); and, if the work +          is an executable linked with the Library, with the complete +          machine-readable "work that uses the Library", as object code +          and/or source code, so that the user can modify the Library +          and then relink to produce a modified executable containing +          the modified Library.  (It is understood that the user who +          changes the contents of definitions files in the Library will +          not necessarily be able to recompile the application to use +          the modified definitions.) + +       b. Accompany the work with a written offer, valid for at least +          three years, to give the same user the materials specified in +          Subsection 6a, above, for a charge no more than the cost of +          performing this distribution. + +       c. If distribution of the work is made by offering access to copy +          from a designated place, offer equivalent access to copy the +          above specified materials from the same place. + +       d. Verify that the user has already received a copy of these +          materials or that you have already sent this user a copy. + +     For an executable, the required form of the "work that uses the +     Library" must include any data and utility programs needed for +     reproducing the executable from it.  However, as a special +     exception, the source code distributed need not include anything +     that is normally distributed (in either source or binary form) +     with the major components (compiler, kernel, and so on) of the +     operating system on which the executable runs, unless that +     component itself accompanies the executable. + +     It may happen that this requirement contradicts the license +     restrictions of other proprietary libraries that do not normally +     accompany the operating system.  Such a contradiction means you +     cannot use both them and the Library together in an executable +     that you distribute. + +  7. You may place library facilities that are a work based on the +     Library side-by-side in a single library together with other +     library facilities not covered by this License, and distribute +     such a combined library, provided that the separate distribution +     of the work based on the Library and of the other library +     facilities is otherwise permitted, and provided that you do these +     two things: + +       a. Accompany the combined library with a copy of the same work +          based on the Library, uncombined with any other library +          facilities.  This must be distributed under the terms of the +          Sections above. + +       b. Give prominent notice with the combined library of the fact +          that part of it is a work based on the Library, and explaining +          where to find the accompanying uncombined form of the same +          work. + +  8. You may not copy, modify, sublicense, link with, or distribute the +     Library except as expressly provided under this License.  Any +     attempt otherwise to copy, modify, sublicense, link with, or +     distribute the Library is void, and will automatically terminate +     your rights under this License.  However, parties who have +     received copies, or rights, from you under this License will not +     have their licenses terminated so long as such parties remain in +     full compliance. + +  9. You are not required to accept this License, since you have not +     signed it.  However, nothing else grants you permission to modify +     or distribute the Library or its derivative works.  These actions +     are prohibited by law if you do not accept this License. +     Therefore, by modifying or distributing the Library (or any work +     based on the Library), you indicate your acceptance of this +     License to do so, and all its terms and conditions for copying, +     distributing or modifying the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +     Library), the recipient automatically receives a license from the +     original licensor to copy, distribute, link with or modify the +     Library subject to these terms and conditions.  You may not impose +     any further restrictions on the recipients' exercise of the rights +     granted herein.  You are not responsible for enforcing compliance +     by third parties to this License. + + 11. If, as a consequence of a court judgment or allegation of patent +     infringement or for any other reason (not limited to patent +     issues), conditions are imposed on you (whether by court order, +     agreement or otherwise) that contradict the conditions of this +     License, they do not excuse you from the conditions of this +     License.  If you cannot distribute so as to satisfy simultaneously +     your obligations under this License and any other pertinent +     obligations, then as a consequence you may not distribute the +     Library at all.  For example, if a patent license would not permit +     royalty-free redistribution of the Library by all those who +     receive copies directly or indirectly through you, then the only +     way you could satisfy both it and this License would be to refrain +     entirely from distribution of the Library. + +     If any portion of this section is held invalid or unenforceable +     under any particular circumstance, the balance of the section is +     intended to apply, and the section as a whole is intended to apply +     in other circumstances. + +     It is not the purpose of this section to induce you to infringe any +     patents or other property right claims or to contest validity of +     any such claims; this section has the sole purpose of protecting +     the integrity of the free software distribution system which is +     implemented by public license practices.  Many people have made +     generous contributions to the wide range of software distributed +     through that system in reliance on consistent application of that +     system; it is up to the author/donor to decide if he or she is +     willing to distribute software through any other system and a +     licensee cannot impose that choice. + +     This section is intended to make thoroughly clear what is believed +     to be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +     certain countries either by patents or by copyrighted interfaces, +     the original copyright holder who places the Library under this +     License may add an explicit geographical distribution limitation +     excluding those countries, so that distribution is permitted only +     in or among countries not thus excluded.  In such case, this +     License incorporates the limitation as if written in the body of +     this License. + + 13. The Free Software Foundation may publish revised and/or new +     versions of the Library General Public License from time to time. +     Such new versions will be similar in spirit to the present version, +     but may differ in detail to address new problems or concerns. + +     Each version is given a distinguishing version number.  If the +     Library specifies a version number of this License which applies +     to it and "any later version", you have the option of following +     the terms and conditions either of that version or of any later +     version published by the Free Software Foundation.  If the Library +     does not specify a license version number, you may choose any +     version ever published by the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +     programs whose distribution conditions are incompatible with these, +     write to the author to ask for permission.  For software which is +     copyrighted by the Free Software Foundation, write to the Free +     Software Foundation; we sometimes make exceptions for this.  Our +     decision will be guided by the two goals of preserving the free +     status of all derivatives of our free software and of promoting +     the sharing and reuse of software generally. + +                                NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +     WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE +     LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +     HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT +     WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT +     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +     FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE +     QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU.  SHOULD THE +     LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY +     SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +     WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY +     MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE +     LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +     INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +     INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF +     DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU +     OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY +     OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN +     ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +                      END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries +============================================== + +   If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change.  You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of +the ordinary General Public License). + +   To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should have +at least the "copyright" line and a pointer to where the full notice is +found. + +     ONE LINE TO GIVE THE LIBRARY'S NAME AND AN IDEA OF WHAT IT DOES. +     Copyright (C) YEAR  NAME OF AUTHOR +      +     This library is free software; you can redistribute it and/or modify it +     under the terms of the GNU Library General Public License as published +     by the Free Software Foundation; either version 2 of the License, or (at +     your option) any later version. +      +     This library 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 +     Library General Public License for more details. +      +     You should have received a copy of the GNU General Public License along +     with this program; if not, write to the Free Software Foundation, Inc., +     59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +   Also add information on how to contact you by electronic and paper +mail. + +   You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary.  Here is a sample; alter the names: + +     Yoyodyne, Inc., hereby disclaims all copyright interest in the library +     `Frob' (a library for tweaking knobs) written by James Random Hacker. +      +     SIGNATURE OF TY COON, 1 April 1990 +     Ty Coon, President of Vice + +   That's all there is to it! + diff --git a/libpthread/linuxthreads/Makefile b/libpthread/linuxthreads/Makefile new file mode 100644 index 000000000..eba08a25d --- /dev/null +++ b/libpthread/linuxthreads/Makefile @@ -0,0 +1,59 @@ +# Makefile for uClibc's pthread library +# +# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Library 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 Library General Public License for more +# details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Makefile for uClibc + +TOPDIR=../../ +include $(TOPDIR)Rules.mak + +#Adjust the soname version to avoid namespace collisions with glibc's libpthread +PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION) +LIBPTHREAD=../libpthread.a + +# set up system dependencies include dirs (NOTE: order matters!) +PTDIR = $(TOPDIR)libpthread/linuxthreads/ +SYSDEPINC = -I$(PTDIR)sysdeps/unix/sysv/linux \ +            -I$(PTDIR)sysdeps/pthread \ +            -I$(PTDIR)sysdeps/unix/sysv \ +            -I$(PTDIR)sysdeps/unix/unix \ +            -I$(PTDIR)sysdeps/$(TARGET_ARCH) \ +            -I$(PTDIR)sysdeps \ +            -I$(TOPDIR)libc/sysdeps/linux/$(TARGET_ARCH) +CFLAGS += $(SYSDEPINC) -DLIBPTHREAD_SO="\"libpthread.so.$(PT_VERSION)\"" -D_GNU_SOURCE + +CSRC=attr.c cancel.c condvar.c errno.c events.c join.c lockfile.c manager.c \ +	mutex.c oldsemaphore.c pt-machine.c ptfork.c pthread.c \ +	ptlongjmp.c rwlock.c semaphore.c signals.c specific.c spinlock.c \ +	wrapsyscall.c #weaks.c no-tsd.c +COBJS=$(patsubst %.c,%.o, $(CSRC)) +OBJS=$(COBJS) + +all: $(OBJS) $(LIBPTHREAD) + +$(LIBPTHREAD): ar-target + +ar-target: $(OBJS) +	$(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS) + +$(COBJS): %.o : %.c +	$(CC) $(CFLAGS) -c $< -o $@ +	$(STRIPTOOL) -x -R .note -R .comment $*.o + +clean: +	rm -f *.[oa] *~ core + + diff --git a/libpthread/linuxthreads/README b/libpthread/linuxthreads/README new file mode 100644 index 000000000..955bd59e7 --- /dev/null +++ b/libpthread/linuxthreads/README @@ -0,0 +1,166 @@ +        Linuxthreads - POSIX 1003.1c kernel threads for Linux + +      Copyright 1996, 1997 Xavier Leroy (Xavier.Leroy@inria.fr) + + +DESCRIPTION: + +This is release 0.7 (late beta) of LinuxThreads, a BiCapitalized +implementation of the Posix 1003.1c "pthread" interface for Linux. + +LinuxThreads provides kernel-level threads: each thread is a separate +Unix process, sharing its address space with the other threads through +the new system call clone(). Scheduling between threads is handled by +the kernel scheduler, just like scheduling between Unix processes. + + +REQUIREMENTS: + +- Linux version 2.0 and up (requires the new clone() system call +  and the new realtime scheduler). + +- For Intel platforms: libc 5.2.18 or later is required. +  5.2.18 or 5.4.12 or later are recommended; +  5.3.12 and 5.4.7 have problems (see the FAQ.html file for more info). + +- Also supports glibc 2 (a.k.a. libc 6), which actually comes with +  a specially-adapted version of this library. + +- Currently supports Intel, Alpha, Sparc, Motorola 68k, ARM and MIPS +  platforms. + +- Multiprocessors are supported. + + +INSTALLATION: + +- Edit the Makefile, set the variables in the "Configuration" section. + +- Do "make". + +- Do "make install". + + +USING LINUXTHREADS: + +        gcc -D_REENTRANT ... -lpthread + +A complete set of manual pages is included. Also see the subdirectory +Examples/ for some sample programs. + + +STATUS: + +- All functions in the Posix 1003.1c base interface implemented. +  Also supports priority scheduling. + +- For users of libc 5 (H.J.Lu's libc), a number of C library functions +  are reimplemented or wrapped to make them thread-safe, including: +  * malloc functions +  * stdio functions (define _REENTRANT before including <stdio.h>) +  * per-thread errno variable (define _REENTRANT before including <errno.h>) +  * directory reading functions (opendir(), etc) +  * sleep() +  * gmtime(), localtime() + +  New library functions provided: +  * flockfile(), funlockfile(), ftrylockfile() +  * reentrant versions of network database functions (gethostbyname_r(), etc) +    and password functions (getpwnam_r(), etc). + +- libc 6 (glibc 2) provides much better thread support than libc 5, +  and comes with a specially-adapted version of LinuxThreads. +  For serious multithreaded programming, you should consider switching +  to glibc 2. It is available from ftp.gnu.org:/pub/gnu and its mirrors. + + +WARNING: + +Many existing libraries are not compatible with LinuxThreads, +either because they are not inherently thread-safe, or because they +have not been compiled with the -D_REENTRANT.  For more info, see the +FAQ.html file in this directory. + +A prime example of the latter is Xlib. If you link it with +LinuxThreads, you'll probably get an "unknown 0 error" very +early. This is just a consequence of the Xlib binaries using the +global variable "errno" to fetch error codes, while LinuxThreads and +the C library use the per-thread "errno" location. + +See the file README.Xfree3.3 for info on how to compile the Xfree 3.3 +libraries to make them compatible with LinuxThreads. + + +KNOWN BUGS AND LIMITATIONS: + +- Threads share pretty much everything they should share according +  to the standard: memory space, file descriptors, signal handlers, +  current working directory, etc. One thing that they do not share +  is their pid's and parent pid's. According to the standard, they +  should have the same, but that's one thing we cannot achieve +  in this implementation (until the CLONE_PID flag to clone() becomes +  usable). + +- The current implementation uses the two signals SIGUSR1 and SIGUSR2, +  so user-level code cannot employ them. Ideally, there should be two +  signals reserved for this library. One signal is used for restarting +  threads blocked on mutexes or conditions; the other is for thread +  cancellation. + +  *** This is not anymore true when the application runs on a kernel +      newer than approximately 2.1.60. + +- The stacks for the threads are allocated high in the memory space, +  below the stack of the initial process, and spaced 2M apart. +  Stacks are allocated with the "grow on demand" flag, so they don't +  use much virtual space initially (4k, currently), but can grow +  up to 2M if needed. + +  Reserving such a large address space for each thread means that, +  on a 32-bit architecture, no more than about 1000 threads can +  coexist (assuming a 2Gb address space for user processes), +  but this is reasonable, since each thread uses up one entry in the +  kernel's process table, which is usually limited to 512 processes. + +  Another potential problem of the "grow on demand" scheme is that +  nothing prevents the user from mmap'ing something in the 2M address +  window reserved for a thread stack, possibly causing later extensions of +  that stack to fail. Mapping at fixed addresses should be avoided +  when using this library. + +- Signal handling does not fully conform to the Posix standard, +  due to the fact that threads are here distinct processes that can be +  sent signals individually, so there's no notion of sending a signal +  to "the" process (the collection of all threads). +  More precisely, here is a summary of the standard requirements +  and how they are met by the implementation: + +  1- Synchronous signals (generated by the thread execution, e.g. SIGFPE) +     are delivered to the thread that raised them. +     (OK.) + +  2- A fatal asynchronous signal terminates all threads in the process. +     (OK. The thread manager notices when a thread dies on a signal +      and kills all other threads with the same signal.) + +  3- An asynchronous signal will be delivered to one of the threads +     of the program which does not block the signal (it is unspecified +     which). +     (No, the signal is delivered to the thread it's been sent to, +      based on the pid of the thread. If that thread is currently +      blocking the signal, the signal remains pending.) + +  4- The signal will be delivered to at most one thread. +     (OK, except for signals generated from the terminal or sent to +      the process group, which will be delivered to all threads.) + +- The current implementation of the MIPS support assumes a MIPS ISA II +  processor or better.  These processors support atomic operations by +  ll/sc instructions.  Older R2000/R3000 series processors are not +  supported yet; support for these will have higher overhead. + +- The current implementation of the ARM support assumes that the SWP +  (atomic swap register with memory) instruction is available.  This is +  the case for all processors except for the ARM1 and ARM2.  On StrongARM, +  the SWP instruction does not bypass the cache, so multi-processor support +  will be more troublesome. diff --git a/libpthread/linuxthreads/README.Xfree3.2 b/libpthread/linuxthreads/README.Xfree3.2 new file mode 100644 index 000000000..ac08e1583 --- /dev/null +++ b/libpthread/linuxthreads/README.Xfree3.2 @@ -0,0 +1,352 @@ +This file describes how to make a threaded X11R6. + +You need the source-code of XFree-3.2. I used the sources of X11R6.1 +(files: xc-1.tar.gz xc-2.tar.gz xc-3.tar.gz) and the patches to +XFree-3.2 (files: README.X11.patch R6.1pl1-3.2.diff.gz cfont32.tgz). + +Untar the xc-?.tar.gz files in a directory called XF3.2 and apply +the XFree-3.2 patches as described in README.X11.patch or use the +whole XFree86 source. + +Now apply the thread patch with + +patch -p0 < XF3.2.xc.diff + +Go to the XF3.2/xc directory and make the whole thing: +nice make World >& world.log & +tail -f world.log + +Wait a few hours or interrupt the process after the shared libs +are made. The shared libs are: + +XF3.2/xc/lib/ICE/libICE.so.6.0* +XF3.2/xc/lib/PEX5/libPEX5.so.6.0* +XF3.2/xc/lib/SM/libSM.so.6.0* +XF3.2/xc/lib/X11/libX11.so.6.1* +XF3.2/xc/lib/XIE/libXIE.so.6.0* +XF3.2/xc/lib/XThrStub/libXThrStub.so.6.0* +XF3.2/xc/lib/Xaw/libXaw.so.6.1* +XF3.2/xc/lib/Xext/libXext.so.6.1* +XF3.2/xc/lib/Xi/libXi.so.6.0* +XF3.2/xc/lib/Xmu/libXmu.so.6.0* +XF3.2/xc/lib/Xt/libXt.so.6.0* +XF3.2/xc/lib/Xtst/libXtst.so.6.1* + +(The Program dga didn't compile, but I have not check out why.) + +Now you can copy the resulting libs    + +cp XF3.2/xc/lib/*/*.so.?.? /usr/X11R6/lib/ + +and create some links + +cd /usr/X11R6/lib/ +ln -s libXThrStub.so.6.0 libXThrStub.so.6 +ln -s libXThrStub.so.6 libXThrStub.so + +or use make install (not tested, and needs new configuration). + +It is possible with the libXThrSub to compile X11 programs without linking +libpthread to them and not necessary to recompile already installed +unthreaded X11 programs, because libXThrSub keeps the dynamic linker quit. +On the other hand you can link libpthread to a X11 program to use threads. + +I used linux 2.0.23 and libc 5.4.7 . + +Hans-Helmut Bühmann     hans@expmech.ing.tu-bs.de + +---------------------------------------------------------------------------- + +XF3.2.xc.diff: +----------------------------------------------------------------------------- +diff -u --recursive XF3.2.orig/xc/config/cf/linux.cf XF3.2/xc/config/cf/linux.cf +--- XF3.2.orig/xc/config/cf/linux.cf	Sun Nov 10 17:05:30 1996 ++++ XF3.2/xc/config/cf/linux.cf	Sun Nov 10 16:30:55 1996 +@@ -61,6 +61,14 @@ + #define HasSnprintf		YES + #endif +  ++#define HasPosixThreads         YES ++#define ThreadedX               YES ++#define BuildThreadStubLibrary	YES ++#define NeedUIThrStubs		YES ++#define HasThreadSafeAPI        NO ++#define SystemMTDefines         -D_REENTRANT ++#define ThreadsLibraries        -lpthread ++ + #define AvoidNullMakeCommand	YES + #define StripInstalledPrograms	YES + #define CompressAllFonts	YES +@@ -158,7 +166,7 @@ + #define LdPostLib		/* Never needed */ +  + #ifdef i386Architecture +-#define OptimizedCDebugFlags	DefaultGcc2i386Opt -m486 ++#define OptimizedCDebugFlags	DefaultGcc2i386Opt -m486 -pipe + #define StandardDefines		-Dlinux -D__i386__ -D_POSIX_SOURCE \ + 				-D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE + #define XawI18nDefines		-DUSE_XWCHAR_STRING -DUSE_XMBTOWC +diff -u --recursive XF3.2.orig/xc/config/cf/lnxLib.tmpl XF3.2/xc/config/cf/lnxLib.tmpl +--- XF3.2.orig/xc/config/cf/lnxLib.tmpl	Sun Nov 10 17:05:30 1996 ++++ XF3.2/xc/config/cf/lnxLib.tmpl	Sat Nov  9 14:52:39 1996 +@@ -19,7 +19,7 @@ +  + #define CplusplusLibC +  +-#define SharedX11Reqs ++#define SharedX11Reqs  	-L$(BUILDLIBDIR) -lXThrStub  + #define SharedOldXReqs	$(LDPRELIB) $(XLIBONLY) + #define SharedXtReqs	$(LDPRELIB) $(XLIBONLY) $(SMLIB) $(ICELIB) + #define SharedXawReqs	$(LDPRELIB) $(XMULIB) $(XTOOLLIB) $(XLIB) +diff -u --recursive XF3.2.orig/xc/include/Xthreads.h XF3.2/xc/include/Xthreads.h +--- XF3.2.orig/xc/include/Xthreads.h	Thu Dec  7 02:19:09 1995 ++++ XF3.2/xc/include/Xthreads.h	Sat Nov  9 01:04:55 1996 +@@ -229,12 +229,12 @@ + #define xcondition_wait(c,m) pthread_cond_wait(c,m) + #define xcondition_signal(c) pthread_cond_signal(c) + #define xcondition_broadcast(c) pthread_cond_broadcast(c) +-#ifdef _DECTHREADS_ ++#if defined(_DECTHREADS_) || defined(linux) + static xthread_t _X_no_thread_id; + #define xthread_have_id(id) !pthread_equal(id, _X_no_thread_id) + #define xthread_clear_id(id) id = _X_no_thread_id + #define xthread_equal(id1,id2) pthread_equal(id1, id2) +-#endif /* _DECTHREADS_ */ ++#endif /* _DECTHREADS_ || linux */ + #if _CMA_VENDOR_ == _CMA__IBM + #ifdef DEBUG			/* too much of a hack to enable normally */ + /* see also cma__obj_set_name() */ +diff -u --recursive XF3.2.orig/xc/lib/X11/util/makekeys.c XF3.2/xc/lib/X11/util/makekeys.c +--- XF3.2.orig/xc/lib/X11/util/makekeys.c	Mon Apr 18 02:22:22 1994 ++++ XF3.2/xc/lib/X11/util/makekeys.c	Sat Nov  9 00:44:14 1996 +@@ -73,7 +73,7 @@ +     register char c; +     int first; +     int best_max_rehash; +-    int best_z; ++    int best_z = 0; +     int num_found; +     KeySym val; +  +diff -u --recursive XF3.2.orig/xc/lib/XThrStub/Imakefile XF3.2/xc/lib/XThrStub/Imakefile +--- XF3.2.orig/xc/lib/XThrStub/Imakefile	Sun Nov 10 17:08:12 1996 ++++ XF3.2/xc/lib/XThrStub/Imakefile	Sat Nov  9 19:04:51 1996 +@@ -25,7 +25,7 @@ +       DEFINES = $(ALLOC_DEFINES) +      INCLUDES =  +          SRCS = $(STUBSRCS) +-         OBJS = $(STUBOBJS ++         OBJS = $(STUBOBJS) +      LINTLIBS = $(LINTXLIB) +  + #include <Library.tmpl> +diff -u --recursive XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c XF3.2/xc/lib/XThrStub/UIThrStubs.c +--- XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c	Sun Nov 10 17:08:12 1996 ++++ XF3.2/xc/lib/XThrStub/UIThrStubs.c	Sun Nov 10 15:14:55 1996 +@@ -37,16 +37,43 @@ +  * specificies the thread library on the link line. +  */ +  ++#if defined(linux) ++#include <pthread.h> ++#else + #include <thread.h> + #include <synch.h> ++#endif +  ++#if defined(linux) ++static pthread_t no_thread_id; ++#endif /* defined(linux) */ ++ ++#if defined(linux) ++#pragma weak pthread_self = _Xthr_self_stub_ ++pthread_t  ++_Xthr_self_stub_() ++{ ++    return(no_thread_id); ++} ++#else /* defined(linux) */ + #pragma weak thr_self = _Xthr_self_stub_ + thread_t  + _Xthr_self_stub_() + { +     return((thread_t)0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_mutex_init = _Xmutex_init_stub_ ++int  ++_Xmutex_init_stub_(m, a) ++    pthread_mutex_t *m; ++    __const pthread_mutexattr_t *a; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak mutex_init = _Xmutex_init_stub_ + int  + _Xmutex_init_stub_(m, t, a) +@@ -56,7 +83,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_mutex_destroy = _Xmutex_destroy_stub_ ++int ++_Xmutex_destroy_stub_(m) ++    pthread_mutex_t *m; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak mutex_destroy = _Xmutex_destroy_stub_ + int + _Xmutex_destroy_stub_(m) +@@ -64,7 +101,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_mutex_lock = _Xmutex_lock_stub_ ++int ++_Xmutex_lock_stub_(m) ++    pthread_mutex_t *m; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak mutex_lock = _Xmutex_lock_stub_ + int + _Xmutex_lock_stub_(m) +@@ -72,7 +119,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_mutex_unlock = _Xmutex_unlock_stub_ ++int ++_Xmutex_unlock_stub_(m) ++    pthread_mutex_t *m; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak mutex_unlock = _Xmutex_unlock_stub_ + int + _Xmutex_unlock_stub_(m) +@@ -80,7 +137,18 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak  pthread_cond_init = _Xcond_init_stub_ ++int  ++_Xcond_init_stub_(c, a) ++    pthread_cond_t *c; ++    __const pthread_condattr_t *a; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak cond_init = _Xcond_init_stub_ + int  + _Xcond_init_stub_(c, t, a) +@@ -90,7 +158,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_cond_destroy = _Xcond_destroy_stub_ ++int ++_Xcond_destroy_stub_(c) ++    pthread_cond_t *c; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak cond_destroy = _Xcond_destroy_stub_ + int + _Xcond_destroy_stub_(c) +@@ -98,7 +176,18 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_cond_wait = _Xcond_wait_stub_ ++int ++_Xcond_wait_stub_(c,m) ++    pthread_cond_t *c; ++    pthread_mutex_t *m; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak cond_wait = _Xcond_wait_stub_ + int + _Xcond_wait_stub_(c,m) +@@ -107,7 +196,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_cond_signal = _Xcond_signal_stub_ ++int ++_Xcond_signal_stub_(c) ++    pthread_cond_t *c; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak cond_signal = _Xcond_signal_stub_ + int + _Xcond_signal_stub_(c) +@@ -115,7 +214,17 @@ + { +     return(0); + } ++#endif /* defined(linux) */ +  ++#if defined(linux) ++#pragma weak pthread_cond_broadcast = _Xcond_broadcast_stub_ ++int ++_Xcond_broadcast_stub_(c) ++    pthread_cond_t *c; ++{ ++    return(0); ++} ++#else /* defined(linux) */ + #pragma weak cond_broadcast = _Xcond_broadcast_stub_ + int + _Xcond_broadcast_stub_(c) +@@ -123,3 +232,15 @@ + { +     return(0); + } ++#endif /* defined(linux) */ ++ ++#if defined(linux) ++#pragma weak pthread_equal = _Xthr_equal_stub_ ++int ++_Xthr_equal_stub_(t1, t2) ++    pthread_t t1; ++    pthread_t t2; ++{ ++    return(1); ++} ++#endif /* defined(linux) */ +------------------------------------------------------------------------- diff --git a/libpthread/linuxthreads/Versions b/libpthread/linuxthreads/Versions new file mode 100644 index 000000000..c0ec79238 --- /dev/null +++ b/libpthread/linuxthreads/Versions @@ -0,0 +1,121 @@ +libc { +  GLIBC_2.0 { +    pthread_attr_destroy; pthread_attr_getdetachstate; +    pthread_attr_getinheritsched; pthread_attr_getschedparam; +    pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init; +    pthread_attr_setdetachstate; pthread_attr_setinheritsched; +    pthread_attr_setschedparam; pthread_attr_setschedpolicy; +    pthread_attr_setscope; pthread_cond_broadcast; pthread_cond_destroy; +    pthread_cond_init; pthread_cond_signal; pthread_cond_wait; +    pthread_condattr_destroy; pthread_condattr_init; pthread_equal; +    pthread_exit; pthread_getschedparam; pthread_mutex_destroy; +    pthread_mutex_init; pthread_mutex_lock; pthread_mutex_unlock; +    pthread_mutexattr_getkind_np; pthread_mutexattr_setkind_np; +    pthread_self; pthread_setcancelstate; pthread_setcanceltype; +    pthread_setschedparam; + +    # Internal libc interface to libpthread +    __libc_internal_tsd_get; __libc_internal_tsd_set; +  } +  GLIBC_2.1 { +    pthread_attr_init; +  } +} + +ld.so { +  GLIBC_2.0 { +    # Internal libc interface to libpthread +    __libc_internal_tsd_get; __libc_internal_tsd_set; +  } +} + +libpthread { +  GLIBC_2.0 { +    # Hidden entry point (through macros). +    _pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push; +    _pthread_cleanup_push_defer; + +    # Internal libc interface to libpthread +    __libc_internal_tsd_get; __libc_internal_tsd_set; + +    # Overwritten libc functions. +    accept; close; connect; fcntl; fork; fsync; longjmp; lseek; msync; +    nanosleep; open; pause; raise; read; recv; recvfrom; recvmsg; send; +    sendmsg; sendto; sigaction; siglongjmp; system; tcdrain; wait; +    waitpid; write; +    __close; __connect; __fcntl; __lseek; __open; __read; __send; __wait; +    __write; +    _IO_flockfile; _IO_ftrylockfile; _IO_funlockfile; +    vfork; __fork; + +    # POSIX.1c extensions to libc. +    flockfile; funlockfile; ftrylockfile; + +    # Non-standard POSIX1.x functions. +    pthread_kill_other_threads_np; pthread_mutexattr_getkind_np; +    pthread_mutexattr_setkind_np; + +    # Real POSIX.1c functions. +    pthread_atfork; pthread_attr_destroy; pthread_attr_getdetachstate; +    pthread_attr_getinheritsched; pthread_attr_getschedparam; +    pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init; +    pthread_attr_setdetachstate; pthread_attr_setinheritsched; +    pthread_attr_setschedparam; pthread_attr_setschedpolicy; +    pthread_attr_setscope; pthread_cancel; pthread_cond_broadcast; +    pthread_cond_destroy; pthread_cond_init; pthread_cond_signal; +    pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy; +    pthread_condattr_init; pthread_create; pthread_detach; pthread_equal; +    pthread_exit; pthread_getschedparam; pthread_getspecific; pthread_join; +    pthread_key_create; pthread_key_delete; pthread_kill; +    pthread_mutex_destroy; pthread_mutex_init; pthread_mutex_lock; +    pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; +    pthread_mutexattr_init; pthread_once; pthread_self; pthread_setcancelstate; +    pthread_setcanceltype; pthread_setschedparam; pthread_setspecific; +    pthread_sigmask; pthread_testcancel; + +    sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait; +    sigwait; + +    # Protected names for functions used in other shared objects. +    __pthread_atfork; __pthread_initialize; __pthread_getspecific; +    __pthread_key_create; __pthread_mutex_destroy; __pthread_mutex_init; +    __pthread_mutex_lock; __pthread_mutex_trylock; __pthread_mutex_unlock; +    __pthread_mutexattr_destroy; __pthread_mutexattr_init; +    __pthread_mutexattr_settype; __pthread_once; __pthread_setspecific; + +    # The error functions. +    __errno_location; __h_errno_location; +  } +  GLIBC_2.1 { +    # Functions with changed interface. +    pthread_attr_init; pthread_create; + +    # Unix98 extensions. +    pthread_rwlock_init; pthread_rwlock_destroy; pthread_rwlock_rdlock; +    pthread_rwlock_tryrdlock; pthread_rwlock_wrlock; pthread_rwlock_trywrlock; +    pthread_rwlock_unlock; pthread_rwlockattr_init; pthread_rwlockattr_destroy; +    pthread_rwlockattr_getpshared; pthread_rwlockattr_setpshared; +    pthread_rwlockattr_getkind_np; pthread_rwlockattr_setkind_np; + +    pthread_attr_getguardsize; pthread_attr_setguardsize; +    pthread_attr_getstackaddr; pthread_attr_setstackaddr; +    pthread_attr_getstacksize; pthread_attr_setstacksize; + +    pthread_getconcurrency; pthread_setconcurrency; + +    pthread_mutexattr_gettype; pthread_mutexattr_settype; + +    sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait; + +    # helper functions +    __libc_current_sigrtmin; __libc_current_sigrtmax; +    __libc_allocate_rtsig; +  } +  GLIBC_2.1.1 { +    sem_close; sem_open; sem_unlink; +  } +  GLIBC_2.1.2 { +    __pthread_kill_other_threads_np; +    __vfork; +  } +} diff --git a/libpthread/linuxthreads/attr.c b/libpthread/linuxthreads/attr.c new file mode 100644 index 000000000..2907a56a8 --- /dev/null +++ b/libpthread/linuxthreads/attr.c @@ -0,0 +1,214 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* changed for uClibc */ +#define __sched_get_priority_min sched_get_priority_min +#define __sched_get_priority_max sched_get_priority_max + +/* Handling of thread attributes */ + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include "pthread.h" +#include "internals.h" + +extern int __getpagesize(void); + +/* NOTE: With uClibc I don't think we need this versioning stuff. + * Therefore, define the function pthread_attr_init() here using + * a strong symbol. */ + +//int __pthread_attr_init_2_1(pthread_attr_t *attr) +int pthread_attr_init(pthread_attr_t *attr) +{ +  size_t ps = __getpagesize (); + +  attr->__detachstate = PTHREAD_CREATE_JOINABLE; +  attr->__schedpolicy = SCHED_OTHER; +  attr->__schedparam.sched_priority = 0; +  attr->__inheritsched = PTHREAD_EXPLICIT_SCHED; +  attr->__scope = PTHREAD_SCOPE_SYSTEM; +  attr->__guardsize = ps; +  attr->__stackaddr = NULL; +  attr->__stackaddr_set = 0; +  attr->__stacksize = STACK_SIZE - ps; +  return 0; +} + +/* uClibc: leave out this for now. */ +#if DO_PTHREAD_VERSIONING_WITH_UCLIBC +#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING +default_symbol_version (__pthread_attr_init_2_1, pthread_attr_init, GLIBC_2.1); + +int __pthread_attr_init_2_0(pthread_attr_t *attr) +{ +  attr->__detachstate = PTHREAD_CREATE_JOINABLE; +  attr->__schedpolicy = SCHED_OTHER; +  attr->__schedparam.sched_priority = 0; +  attr->__inheritsched = PTHREAD_EXPLICIT_SCHED; +  attr->__scope = PTHREAD_SCOPE_SYSTEM; +  return 0; +} +symbol_version (__pthread_attr_init_2_0, pthread_attr_init, GLIBC_2.0); +#else +strong_alias (__pthread_attr_init_2_1, pthread_attr_init) +#endif +#endif /* DO_PTHREAD_VERSIONING_WITH_UCLIBC */ + +int pthread_attr_destroy(pthread_attr_t *attr) +{ +  return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +{ +  if (detachstate < PTHREAD_CREATE_JOINABLE || +      detachstate > PTHREAD_CREATE_DETACHED) +    return EINVAL; +  attr->__detachstate = detachstate; +  return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +{ +  *detachstate = attr->__detachstate; +  return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, +                               const struct sched_param *param) +{ +  int max_prio = __sched_get_priority_max(attr->__schedpolicy); +  int min_prio = __sched_get_priority_min(attr->__schedpolicy); + +  if (param->sched_priority < min_prio || param->sched_priority > max_prio) +    return EINVAL; +  memcpy (&attr->__schedparam, param, sizeof (struct sched_param)); +  return 0; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, +                               struct sched_param *param) +{ +  memcpy (param, &attr->__schedparam, sizeof (struct sched_param)); +  return 0; +} + +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ +  if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR) +    return EINVAL; +  attr->__schedpolicy = policy; +  return 0; +} + +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) +{ +  *policy = attr->__schedpolicy; +  return 0; +} + +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) +{ +  if (inherit != PTHREAD_INHERIT_SCHED && inherit != PTHREAD_EXPLICIT_SCHED) +    return EINVAL; +  attr->__inheritsched = inherit; +  return 0; +} + +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit) +{ +  *inherit = attr->__inheritsched; +  return 0; +} + +int pthread_attr_setscope(pthread_attr_t *attr, int scope) +{ +  switch (scope) { +  case PTHREAD_SCOPE_SYSTEM: +    attr->__scope = scope; +    return 0; +  case PTHREAD_SCOPE_PROCESS: +    return ENOTSUP; +  default: +    return EINVAL; +  } +} + +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) +{ +  *scope = attr->__scope; +  return 0; +} + +int __pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +{ +  size_t ps = __getpagesize (); + +  /* First round up the guard size.  */ +  guardsize = roundup (guardsize, ps); + +  /* The guard size must not be larger than the stack itself */ +  if (guardsize >= attr->__stacksize) return EINVAL; + +  attr->__guardsize = guardsize; + +  return 0; +} +weak_alias (__pthread_attr_setguardsize, pthread_attr_setguardsize) + +int __pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) +{ +  *guardsize = attr->__guardsize; +  return 0; +} +weak_alias (__pthread_attr_getguardsize, pthread_attr_getguardsize) + +int __pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) +{ +  attr->__stackaddr = stackaddr; +  attr->__stackaddr_set = 1; +  return 0; +} +weak_alias (__pthread_attr_setstackaddr, pthread_attr_setstackaddr) + +int __pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) +{ +  /* XXX This function has a stupid definition.  The standard specifies +     no error value but what is if no stack address was set?  We simply +     return the value we have in the member.  */ +  *stackaddr = attr->__stackaddr; +  return 0; +} +weak_alias (__pthread_attr_getstackaddr, pthread_attr_getstackaddr) + +int __pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +{ +  /* We don't accept value smaller than PTHREAD_STACK_MIN.  */ +  if (stacksize < PTHREAD_STACK_MIN) +    return EINVAL; + +  attr->__stacksize = stacksize; +  return 0; +} +weak_alias (__pthread_attr_setstacksize, pthread_attr_setstacksize) + +int __pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) +{ +  *stacksize = attr->__stacksize; +  return 0; +} +weak_alias (__pthread_attr_getstacksize, pthread_attr_getstacksize) diff --git a/libpthread/linuxthreads/cancel.c b/libpthread/linuxthreads/cancel.c new file mode 100644 index 000000000..8fd8c1e60 --- /dev/null +++ b/libpthread/linuxthreads/cancel.c @@ -0,0 +1,171 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Thread cancellation */ + +#include <errno.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" + +int pthread_setcancelstate(int state, int * oldstate) +{ +  pthread_descr self = thread_self(); +  if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE) +    return EINVAL; +  if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate); +  THREAD_SETMEM(self, p_cancelstate, state); +  if (THREAD_GETMEM(self, p_canceled) && +      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && +      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) +    pthread_exit(PTHREAD_CANCELED); +  return 0; +} + +int pthread_setcanceltype(int type, int * oldtype) +{ +  pthread_descr self = thread_self(); +  if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS) +    return EINVAL; +  if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype); +  THREAD_SETMEM(self, p_canceltype, type); +  if (THREAD_GETMEM(self, p_canceled) && +      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && +      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) +    pthread_exit(PTHREAD_CANCELED); +  return 0; +} + +int pthread_cancel(pthread_t thread) +{ +  pthread_handle handle = thread_handle(thread); +  int pid; +  int dorestart = 0; +  pthread_descr th; +  pthread_extricate_if *pextricate; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, thread)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } + +  th = handle->h_descr; + +  if (th->p_canceled) { +    __pthread_unlock(&handle->h_lock); +    return 0; +  } + +  pextricate = th->p_extricate; +  th->p_canceled = 1; +  pid = th->p_pid; + +  /* If the thread has registered an extrication interface, then +     invoke the interface. If it returns 1, then we succeeded in +     dequeuing the thread from whatever waiting object it was enqueued +     with. In that case, it is our responsibility to wake it up.  +     And also to set the p_woken_by_cancel flag so the woken thread +     can tell that it was woken by cancellation. */ + +  if (pextricate != NULL) { +    dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th); +    th->p_woken_by_cancel = dorestart; +  } + +  __pthread_unlock(&handle->h_lock); + +  /* If the thread has suspended or is about to, then we unblock it by +     issuing a restart, instead of a cancel signal. Otherwise we send +     the cancel signal to unblock the thread from a cancellation point, +     or to initiate asynchronous cancellation. The restart is needed so +     we have proper accounting of restarts; suspend decrements the thread's +     resume count, and restart() increments it.  This also means that suspend's +     handling of the cancel signal is obsolete. */ + +  if (dorestart) +    restart(th); +  else  +    kill(pid, __pthread_sig_cancel); + +  return 0; +} + +void pthread_testcancel(void) +{ +  pthread_descr self = thread_self(); +  if (THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) +    pthread_exit(PTHREAD_CANCELED); +} + +void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer, +			   void (*routine)(void *), void * arg) +{ +  pthread_descr self = thread_self(); +  buffer->__routine = routine; +  buffer->__arg = arg; +  buffer->__prev = THREAD_GETMEM(self, p_cleanup); +  THREAD_SETMEM(self, p_cleanup, buffer); +} + +void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer, +			  int execute) +{ +  pthread_descr self = thread_self(); +  if (execute) buffer->__routine(buffer->__arg); +  THREAD_SETMEM(self, p_cleanup, buffer->__prev); +} + +void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer, +				 void (*routine)(void *), void * arg) +{ +  pthread_descr self = thread_self(); +  buffer->__routine = routine; +  buffer->__arg = arg; +  buffer->__canceltype = THREAD_GETMEM(self, p_canceltype); +  buffer->__prev = THREAD_GETMEM(self, p_cleanup); +  THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED); +  THREAD_SETMEM(self, p_cleanup, buffer); +} + +void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer, +				  int execute) +{ +  pthread_descr self = thread_self(); +  if (execute) buffer->__routine(buffer->__arg); +  THREAD_SETMEM(self, p_cleanup, buffer->__prev); +  THREAD_SETMEM(self, p_canceltype, buffer->__canceltype); +  if (THREAD_GETMEM(self, p_canceled) && +      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && +      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) +    pthread_exit(PTHREAD_CANCELED); +} + +void __pthread_perform_cleanup(void) +{ +  pthread_descr self = thread_self(); +  struct _pthread_cleanup_buffer * c; +  for (c = THREAD_GETMEM(self, p_cleanup); c != NULL; c = c->__prev) +    c->__routine(c->__arg); +} + +#ifndef PIC +/* We need a hook to force the cancelation wrappers to be linked in when +   static libpthread is used.  */ +extern const int __pthread_provide_wrappers; +static const int * const __pthread_require_wrappers = +  &__pthread_provide_wrappers; +#endif diff --git a/libpthread/linuxthreads/condvar.c b/libpthread/linuxthreads/condvar.c new file mode 100644 index 000000000..85754a18f --- /dev/null +++ b/libpthread/linuxthreads/condvar.c @@ -0,0 +1,417 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/* and Pavel Krauz (krauz@fsid.cvut.cz).                                */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Condition variables */ + +#include <errno.h> +#include <sched.h> +#include <stddef.h> +#include <sys/time.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "queue.h" +#include "restart.h" + +static int pthread_cond_timedwait_relative_old(pthread_cond_t *, +    pthread_mutex_t *, const struct timespec *); + +static int pthread_cond_timedwait_relative_new(pthread_cond_t *, +    pthread_mutex_t *, const struct timespec *); + +static int (*pthread_cond_tw_rel)(pthread_cond_t *, pthread_mutex_t *, +    const struct timespec *) = pthread_cond_timedwait_relative_old; + +/* initialize this module */ +void __pthread_init_condvar(int rt_sig_available) +{ +  if (rt_sig_available) +    pthread_cond_tw_rel = pthread_cond_timedwait_relative_new; +} + +int pthread_cond_init(pthread_cond_t *cond, +                      const pthread_condattr_t *cond_attr) +{ +  __pthread_init_lock(&cond->__c_lock); +  cond->__c_waiting = NULL; +  return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) +{ +  if (cond->__c_waiting != NULL) return EBUSY; +  return 0; +} + +/* Function called by pthread_cancel to remove the thread from +   waiting on a condition variable queue. */ + +static int cond_extricate_func(void *obj, pthread_descr th) +{ +  volatile pthread_descr self = thread_self(); +  pthread_cond_t *cond = obj; +  int did_remove = 0; + +  __pthread_lock(&cond->__c_lock, self); +  did_remove = remove_from_queue(&cond->__c_waiting, th); +  __pthread_unlock(&cond->__c_lock); + +  return did_remove; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ +  volatile pthread_descr self = thread_self(); +  pthread_extricate_if extr; +  int already_canceled = 0; + +  /* Set up extrication interface */ +  extr.pu_object = cond; +  extr.pu_extricate_func = cond_extricate_func; + +  /* Register extrication interface */ +  __pthread_set_own_extricate_if(self, &extr); + +  /* Atomically enqueue thread for waiting, but only if it is not +     canceled. If the thread is canceled, then it will fall through the +     suspend call below, and then call pthread_exit without +     having to worry about whether it is still on the condition variable queue. +     This depends on pthread_cancel setting p_canceled before calling the +     extricate function. */ + +  __pthread_lock(&cond->__c_lock, self); +  if (!(THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) +    enqueue(&cond->__c_waiting, self); +  else +    already_canceled = 1; +  __pthread_unlock(&cond->__c_lock); + +  if (already_canceled) { +    __pthread_set_own_extricate_if(self, 0); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_unlock(mutex); + +  suspend(self); +  __pthread_set_own_extricate_if(self, 0); + +  /* Check for cancellation again, to provide correct cancellation +     point behavior */ + +  if (THREAD_GETMEM(self, p_woken_by_cancel) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +    THREAD_SETMEM(self, p_woken_by_cancel, 0); +    pthread_mutex_lock(mutex); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_lock(mutex); +  return 0; +} + +/* The following function is used on kernels that don't have rt signals. +   SIGUSR1 is used as the restart signal. The different code is needed +   because that ordinary signal does not queue. */ + +static int +pthread_cond_timedwait_relative_old(pthread_cond_t *cond, +				pthread_mutex_t *mutex, +				const struct timespec * abstime) +{ +  volatile pthread_descr self = thread_self(); +  sigset_t unblock, initial_mask; +  int already_canceled = 0; +  int was_signalled = 0; +  sigjmp_buf jmpbuf; +  pthread_extricate_if extr; + +  /* Set up extrication interface */ +  extr.pu_object = cond; +  extr.pu_extricate_func = cond_extricate_func; + +  /* Register extrication interface */ +  __pthread_set_own_extricate_if(self, &extr); + +  /* Enqueue to wait on the condition and check for cancellation. */ +  __pthread_lock(&cond->__c_lock, self); +  if (!(THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) +    enqueue(&cond->__c_waiting, self); +  else +    already_canceled = 1; +  __pthread_unlock(&cond->__c_lock); + +  if (already_canceled) { +    __pthread_set_own_extricate_if(self, 0); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_unlock(mutex); + +  if (atomic_decrement(&self->p_resume_count) == 0) { +    /* Set up a longjmp handler for the restart signal, unblock +       the signal and sleep. */ + +    if (sigsetjmp(jmpbuf, 1) == 0) { +      THREAD_SETMEM(self, p_signal_jmp, &jmpbuf); +      THREAD_SETMEM(self, p_signal, 0); +      /* Unblock the restart signal */ +      sigemptyset(&unblock); +      sigaddset(&unblock, __pthread_sig_restart); +      sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask); + +      while (1) { +	struct timeval now; +	struct timespec reltime; + +	/* Compute a time offset relative to now.  */ +	gettimeofday (&now, NULL); +	reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000; +	reltime.tv_sec = abstime->tv_sec - now.tv_sec; +	if (reltime.tv_nsec < 0) { +	  reltime.tv_nsec += 1000000000; +	  reltime.tv_sec -= 1; +	} + +	/* Sleep for the required duration. If woken by a signal, resume waiting +	   as required by Single Unix Specification.  */ +	if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0) +	  break; +      } + +      /* Block the restart signal again */ +      sigprocmask(SIG_SETMASK, &initial_mask, NULL); +      was_signalled = 0; +    } else { +      was_signalled = 1; +    } +    THREAD_SETMEM(self, p_signal_jmp, NULL); +  } + +  /* Now was_signalled is true if we exited the above code +     due to the delivery of a restart signal.  In that case, +     we know we have been dequeued and resumed and that the +     resume count is balanced.  Otherwise, there are some +     cases to consider. First, try to bump up the resume count +     back to zero. If it goes to 1, it means restart() was +     invoked on this thread. The signal must be consumed +     and the count bumped down and everything is cool. +     Otherwise, no restart was delivered yet, so we remove +     the thread from the queue. If this succeeds, it's a clear +     case of timeout. If we fail to remove from the queue, then we +     must wait for a restart. */ + +  if (!was_signalled) { +    if (atomic_increment(&self->p_resume_count) != -1) { +      __pthread_wait_for_restart_signal(self); +      atomic_decrement(&self->p_resume_count); /* should be zero now! */ +    } else { +      int was_on_queue; +      __pthread_lock(&cond->__c_lock, self); +      was_on_queue = remove_from_queue(&cond->__c_waiting, self); +      __pthread_unlock(&cond->__c_lock); + +      if (was_on_queue) { +	__pthread_set_own_extricate_if(self, 0); +	pthread_mutex_lock(mutex); +	return ETIMEDOUT; +      } + +      suspend(self); +    } +  } + +  __pthread_set_own_extricate_if(self, 0); + +  /* The remaining logic is the same as in other cancellable waits, +     such as pthread_join sem_wait or pthread_cond wait. */ + +  if (THREAD_GETMEM(self, p_woken_by_cancel) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +    THREAD_SETMEM(self, p_woken_by_cancel, 0); +    pthread_mutex_lock(mutex); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_lock(mutex); +  return 0; +} + +/* The following function is used on new (late 2.1 and 2.2 and higher) kernels +   that have rt signals which queue. */ + +static int +pthread_cond_timedwait_relative_new(pthread_cond_t *cond, +				pthread_mutex_t *mutex, +				const struct timespec * abstime) +{ +  volatile pthread_descr self = thread_self(); +  sigset_t unblock, initial_mask; +  int already_canceled = 0; +  int was_signalled = 0; +  sigjmp_buf jmpbuf; +  pthread_extricate_if extr; + +  /* Set up extrication interface */ +  extr.pu_object = cond; +  extr.pu_extricate_func = cond_extricate_func; + +  /* Register extrication interface */ +  __pthread_set_own_extricate_if(self, &extr); + +  /* Enqueue to wait on the condition and check for cancellation. */ +  __pthread_lock(&cond->__c_lock, self); +  if (!(THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) +    enqueue(&cond->__c_waiting, self); +  else +    already_canceled = 1; +  __pthread_unlock(&cond->__c_lock); + +  if (already_canceled) { +    __pthread_set_own_extricate_if(self, 0); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_unlock(mutex); + +  /* Set up a longjmp handler for the restart signal, unblock +     the signal and sleep. */ + +  if (sigsetjmp(jmpbuf, 1) == 0) { +    THREAD_SETMEM(self, p_signal_jmp, &jmpbuf); +    THREAD_SETMEM(self, p_signal, 0); +    /* Unblock the restart signal */ +    sigemptyset(&unblock); +    sigaddset(&unblock, __pthread_sig_restart); +    sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask); + +    while (1) { +	struct timeval now; +	struct timespec reltime; + +	/* Compute a time offset relative to now.  */ +	gettimeofday (&now, NULL); +	reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000; +	reltime.tv_sec = abstime->tv_sec - now.tv_sec; +	if (reltime.tv_nsec < 0) { +	  reltime.tv_nsec += 1000000000; +	  reltime.tv_sec -= 1; +	} + +	/* Sleep for the required duration. If woken by a signal, +	   resume waiting as required by Single Unix Specification.  */ +	if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0) +	  break; +      } + +    /* Block the restart signal again */ +    sigprocmask(SIG_SETMASK, &initial_mask, NULL); +    was_signalled = 0; +  } else { +    was_signalled = 1; +  } +  THREAD_SETMEM(self, p_signal_jmp, NULL); + +  /* Now was_signalled is true if we exited the above code +     due to the delivery of a restart signal.  In that case, +     everything is cool. We have been removed from the queue +     by the other thread, and consumed its signal. + +     Otherwise we this thread woke up spontaneously, or due to a signal other +     than restart. The next thing to do is to try to remove the thread +     from the queue. This may fail due to a race against another thread +     trying to do the same. In the failed case, we know we were signalled, +     and we may also have to consume a restart signal. */ + +  if (!was_signalled) { +    int was_on_queue; + +    /* __pthread_lock will queue back any spurious restarts that +       may happen to it. */ + +    __pthread_lock(&cond->__c_lock, self); +    was_on_queue = remove_from_queue(&cond->__c_waiting, self); +    __pthread_unlock(&cond->__c_lock); + +    if (was_on_queue) { +      __pthread_set_own_extricate_if(self, 0); +      pthread_mutex_lock(mutex); +      return ETIMEDOUT; +    } + +    /* Eat the outstanding restart() from the signaller */ +    suspend(self); +  } + +  __pthread_set_own_extricate_if(self, 0); + +  /* The remaining logic is the same as in other cancellable waits, +     such as pthread_join sem_wait or pthread_cond wait. */ + +  if (THREAD_GETMEM(self, p_woken_by_cancel) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +    THREAD_SETMEM(self, p_woken_by_cancel, 0); +    pthread_mutex_lock(mutex); +    pthread_exit(PTHREAD_CANCELED); +  } + +  pthread_mutex_lock(mutex); +  return 0; +} + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, +                           const struct timespec * abstime) +{ +  /* Indirect call through pointer! */ +  return pthread_cond_tw_rel(cond, mutex, abstime); +} + +int pthread_cond_signal(pthread_cond_t *cond) +{ +  pthread_descr th; + +  __pthread_lock(&cond->__c_lock, NULL); +  th = dequeue(&cond->__c_waiting); +  __pthread_unlock(&cond->__c_lock); +  if (th != NULL) restart(th); +  return 0; +} + +int pthread_cond_broadcast(pthread_cond_t *cond) +{ +  pthread_descr tosignal, th; + +  __pthread_lock(&cond->__c_lock, NULL); +  /* Copy the current state of the waiting queue and empty it */ +  tosignal = cond->__c_waiting; +  cond->__c_waiting = NULL; +  __pthread_unlock(&cond->__c_lock); +  /* Now signal each process in the queue */ +  while ((th = dequeue(&tosignal)) != NULL) restart(th); +  return 0; +} + +int pthread_condattr_init(pthread_condattr_t *attr) +{ +  return 0; +} + +int pthread_condattr_destroy(pthread_condattr_t *attr) +{ +  return 0; +} diff --git a/libpthread/linuxthreads/configure b/libpthread/linuxthreads/configure new file mode 100644 index 000000000..3eafc93f5 --- /dev/null +++ b/libpthread/linuxthreads/configure @@ -0,0 +1,5 @@ +# This is only to keep the GNU C library configure mechanism happy. +# +# Perhaps some day we need a real configuration script for different +# kernel versions or so. +exit 0 diff --git a/libpthread/linuxthreads/debug.h b/libpthread/linuxthreads/debug.h new file mode 100644 index 000000000..79acb23c0 --- /dev/null +++ b/libpthread/linuxthreads/debug.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** NAME: +** debug.h +** +** DESCRIPTION: +** This header file defines the debug macros used in pthreads. To turn +** debugging on, add -DDEBUG_PT to CFLAGS. It was added to the original +** distribution of linuxthreads. +** +** This program is free software; you can redistribute it and/or         +** modify it under the terms of the GNU Library 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 Library General Public License for more details.                  +** +****************************************************************************/ + +#ifndef _PT_DEBUG_H +#define _PT_DEBUG_H + +/* include asserts for now */ +#define DO_ASSERT + +/* define the PDEBUG macro here */ +#undef PDEBUG +#ifdef DEBUG_PT +#  define PDEBUG(fmt, args...) __pthread_message(__FUNCTION__": " fmt, ## args) +#else +#  define PDEBUG(fmt, args...) /* debug switched off */ +#endif + +/* nothing; placeholder to disable a PDEBUG message but don't delete it */ +#undef PDEBUGG +#define PDEBUGG(fmt, args...)  + +/* Define ASSERT to stop/warn. Should be void in production code */ +#undef ASSERT +#ifdef DO_ASSERT +#  define ASSERT(x) if (!(x)) fprintf(stderr, "pt: assertion failed in %s:%i.\n",\ +                    __FILE__, __LINE__) +#else +#  define ASSERT(x) +#endif + +#endif /* _PT_DEBUG_H */ diff --git a/libpthread/linuxthreads/errno.c b/libpthread/linuxthreads/errno.c new file mode 100644 index 000000000..ad43be47a --- /dev/null +++ b/libpthread/linuxthreads/errno.c @@ -0,0 +1,34 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Define the location of errno for the remainder of the C library */ + +#define __FORCE_GLIBC +#include <features.h> +#include <errno.h> +#include <netdb.h> +#include "pthread.h" +#include "internals.h" + +int * __errno_location() +{ +  pthread_descr self = thread_self(); +  return THREAD_GETMEM (self, p_errnop); +} + +int * __h_errno_location() +{ +  pthread_descr self = thread_self(); +  return THREAD_GETMEM (self, p_h_errnop); +} diff --git a/libpthread/linuxthreads/events.c b/libpthread/linuxthreads/events.c new file mode 100644 index 000000000..e5be3d935 --- /dev/null +++ b/libpthread/linuxthreads/events.c @@ -0,0 +1,35 @@ +/* Event functions used while debugging. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +/* The functions contained here do nothing, they just return.  */ + +void +__linuxthreads_create_event (void) +{ +} + +void +__linuxthreads_death_event (void) +{ +} + +void +__linuxthreads_reap_event (void) +{ +} diff --git a/libpthread/linuxthreads/internals.h b/libpthread/linuxthreads/internals.h new file mode 100644 index 000000000..933ccb5f3 --- /dev/null +++ b/libpthread/linuxthreads/internals.h @@ -0,0 +1,480 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +#ifndef _INTERNALS_H +#define _INTERNALS_H   1 + +/* Internal data structures */ + +/* Includes */ + +#include <bits/libc-tsd.h> /* for _LIBC_TSD_KEY_N */ +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> +#include "pt-machine.h" +#include "semaphore.h" +#include "../linuxthreads_db/thread_dbP.h" + +#ifndef THREAD_GETMEM +# define THREAD_GETMEM(descr, member) descr->member +#endif +#ifndef THREAD_GETMEM_NC +# define THREAD_GETMEM_NC(descr, member) descr->member +#endif +#ifndef THREAD_SETMEM +# define THREAD_SETMEM(descr, member, value) descr->member = (value) +#endif +#ifndef THREAD_SETMEM_NC +# define THREAD_SETMEM_NC(descr, member, value) descr->member = (value) +#endif + +/* Arguments passed to thread creation routine */ + +struct pthread_start_args { +  void * (*start_routine)(void *); /* function to run */ +  void * arg;                   /* its argument */ +  sigset_t mask;                /* initial signal mask for thread */ +  int schedpolicy;              /* initial scheduling policy (if any) */ +  struct sched_param schedparam; /* initial scheduling parameters (if any) */ +}; + + +/* We keep thread specific data in a special data structure, a two-level +   array.  The top-level array contains pointers to dynamically allocated +   arrays of a certain number of data pointers.  So we can implement a +   sparse array.  Each dynamic second-level array has +	PTHREAD_KEY_2NDLEVEL_SIZE +   entries.  This value shouldn't be too large.  */ +#define PTHREAD_KEY_2NDLEVEL_SIZE	32 + +/* We need to address PTHREAD_KEYS_MAX key with PTHREAD_KEY_2NDLEVEL_SIZE +   keys in each subarray.  */ +#define PTHREAD_KEY_1STLEVEL_SIZE \ +  ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) \ +   / PTHREAD_KEY_2NDLEVEL_SIZE) + +typedef void (*destr_function)(void *); + +struct pthread_key_struct { +  int in_use;                   /* already allocated? */ +  destr_function destr;         /* destruction routine */ +}; + + +#define PTHREAD_START_ARGS_INITIALIZER { NULL, NULL, {{0, }}, 0, { 0 } } + +/* The type of thread descriptors */ + +typedef struct _pthread_descr_struct * pthread_descr; + +/* Callback interface for removing the thread from waiting on an +   object if it is cancelled while waiting or about to wait. +   This hold a pointer to the object, and a pointer to a function +   which ``extricates'' the thread from its enqueued state. +   The function takes two arguments: pointer to the wait object, +   and a pointer to the thread. It returns 1 if an extrication +   actually occured, and hence the thread must also be signalled. +   It returns 0 if the thread had already been extricated. */ + +typedef struct _pthread_extricate_struct { +    void *pu_object; +    int (*pu_extricate_func)(void *, pthread_descr); +} pthread_extricate_if; + +/* Atomic counter made possible by compare_and_swap */ + +struct pthread_atomic { +  long p_count; +  int p_spinlock; +}; + +/* Context info for read write locks. The pthread_rwlock_info structure +   is information about a lock that has been read-locked by the thread +   in whose list this structure appears. The pthread_rwlock_context +   is embedded in the thread context and contains a pointer to the +   head of the list of lock info structures, as well as a count of +   read locks that are untracked, because no info structure could be +   allocated for them. */ + +struct _pthread_rwlock_t; + +typedef struct _pthread_rwlock_info { +  struct _pthread_rwlock_info *pr_next; +  struct _pthread_rwlock_t *pr_lock; +  int pr_lock_count; +} pthread_readlock_info; + +struct _pthread_descr_struct { +  pthread_descr p_nextlive, p_prevlive; +                                /* Double chaining of active threads */ +  pthread_descr p_nextwaiting;  /* Next element in the queue holding the thr */ +  pthread_descr p_nextlock;	/* can be on a queue and waiting on a lock */ +  pthread_t p_tid;              /* Thread identifier */ +  int p_pid;                    /* PID of Unix process */ +  int p_priority;               /* Thread priority (== 0 if not realtime) */ +  struct _pthread_fastlock * p_lock; /* Spinlock for synchronized accesses */ +  int p_signal;                 /* last signal received */ +  sigjmp_buf * p_signal_jmp;    /* where to siglongjmp on a signal or NULL */ +  sigjmp_buf * p_cancel_jmp;    /* where to siglongjmp on a cancel or NULL */ +  char p_terminated;            /* true if terminated e.g. by pthread_exit */ +  char p_detached;              /* true if detached */ +  char p_exited;                /* true if the assoc. process terminated */ +  void * p_retval;              /* placeholder for return value */ +  int p_retcode;                /* placeholder for return code */ +  pthread_descr p_joining;      /* thread joining on that thread or NULL */ +  struct _pthread_cleanup_buffer * p_cleanup; /* cleanup functions */ +  char p_cancelstate;           /* cancellation state */ +  char p_canceltype;            /* cancellation type (deferred/async) */ +  char p_canceled;              /* cancellation request pending */ +  int * p_errnop;               /* pointer to used errno variable */ +  int p_errno;                  /* error returned by last system call */ +  int * p_h_errnop;             /* pointer to used h_errno variable */ +  int p_h_errno;                /* error returned by last netdb function */ +  char * p_in_sighandler;       /* stack address of sighandler, or NULL */ +  char p_sigwaiting;            /* true if a sigwait() is in progress */ +  struct pthread_start_args p_start_args; /* arguments for thread creation */ +  void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE]; /* thread-specific data */ +  void * p_libc_specific[_LIBC_TSD_KEY_N]; /* thread-specific data for libc */ +  int p_userstack;		/* nonzero if the user provided the stack */ +  void *p_guardaddr;		/* address of guard area or NULL */ +  size_t p_guardsize;		/* size of guard area */ +  pthread_descr p_self;		/* Pointer to this structure */ +  int p_nr;                     /* Index of descriptor in __pthread_handles */ +  int p_report_events;         /* Nonzero if events must be reported.  */ +  td_eventbuf_t p_eventbuf;     /* Data for event.  */ +  struct pthread_atomic p_resume_count; /* number of times restart() was +					   called on thread */ +  char p_woken_by_cancel;       /* cancellation performed wakeup */ +  pthread_extricate_if *p_extricate; /* See above */ +  pthread_readlock_info *p_readlock_list;  /* List of readlock info structs */ +  pthread_readlock_info *p_readlock_free;  /* Free list of structs */ +  int p_untracked_readlock_count;	/* Readlocks not tracked by list */ +  /* New elements must be added at the end.  */ +} __attribute__ ((aligned(32))); /* We need to align the structure so that +				    doubles are aligned properly.  This is 8 +				    bytes on MIPS and 16 bytes on MIPS64. +				    32 bytes might give better cache +				    utilization.  */ + +/* The type of thread handles. */ + +typedef struct pthread_handle_struct * pthread_handle; + +struct pthread_handle_struct { +  struct _pthread_fastlock h_lock; /* Fast lock for sychronized access */ +  pthread_descr h_descr;        /* Thread descriptor or NULL if invalid */ +  char * h_bottom;              /* Lowest address in the stack thread */ +}; + +/* The type of messages sent to the thread manager thread */ + +struct pthread_request { +  pthread_descr req_thread;     /* Thread doing the request */ +  enum {                        /* Request kind */ +    REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT, +    REQ_POST, REQ_DEBUG +  } req_kind; +  union {                       /* Arguments for request */ +    struct {                    /* For REQ_CREATE: */ +      const pthread_attr_t * attr; /* thread attributes */ +      void * (*fn)(void *);     /*   start function */ +      void * arg;               /*   argument to start function */ +      sigset_t mask;            /*   signal mask */ +    } create; +    struct {                    /* For REQ_FREE: */ +      pthread_t thread_id;      /*   identifier of thread to free */ +    } free; +    struct {                    /* For REQ_PROCESS_EXIT: */ +      int code;                 /*   exit status */ +    } exit; +    void * post;                /* For REQ_POST: the semaphore */ +  } req_args; +}; + + +/* Signals used for suspend/restart and for cancellation notification.  */ + +extern int __pthread_sig_restart; +extern int __pthread_sig_cancel; + +/* Signal used for interfacing with gdb */ + +extern int __pthread_sig_debug; + +/* Global array of thread handles, used for validating a thread id +   and retrieving the corresponding thread descriptor. Also used for +   mapping the available stack segments. */ + +extern struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX]; + +/* Descriptor of the initial thread */ + +extern struct _pthread_descr_struct __pthread_initial_thread; + +/* Descriptor of the manager thread */ + +extern struct _pthread_descr_struct __pthread_manager_thread; + +/* Descriptor of the main thread */ + +extern pthread_descr __pthread_main_thread; + +/* Limit between the stack of the initial thread (above) and the +   stacks of other threads (below). Aligned on a STACK_SIZE boundary. +   Initially 0, meaning that the current thread is (by definition) +   the initial thread. */ + +/* For non-MMU systems also remember to stack top of the initial thread. + * This is adapted when other stacks are malloc'ed since we don't know + * the bounds a-priori. -StS */ + +extern char *__pthread_initial_thread_bos; +#ifndef __UCLIBC_HAS_MMU__ +extern char *__pthread_initial_thread_tos; +#define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) if ((tos)>=__pthread_initial_thread_bos && (bos)<=__pthread_initial_thread_tos) __pthread_initial_thread_bos = (tos)+1 +#else +#define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) /* empty */ +#endif /* __UCLIBC_HAS_MMU__ */ + + +/* Indicate whether at least one thread has a user-defined stack (if 1), +   or all threads have stacks supplied by LinuxThreads (if 0). */ + +extern int __pthread_nonstandard_stacks; + +/* File descriptor for sending requests to the thread manager. +   Initially -1, meaning that __pthread_initialize_manager must be called. */ + +extern int __pthread_manager_request; + +/* Other end of the pipe for sending requests to the thread manager. */ + +extern int __pthread_manager_reader; + +/* Limits of the thread manager stack. */ + +extern char *__pthread_manager_thread_bos; +extern char *__pthread_manager_thread_tos; + +/* Pending request for a process-wide exit */ + +extern int __pthread_exit_requested, __pthread_exit_code; + +/* Set to 1 by gdb if we're debugging */ + +extern volatile int __pthread_threads_debug; + +/* Globally enabled events.  */ +extern volatile td_thr_events_t __pthread_threads_events; + +/* Pointer to descriptor of thread with last event.  */ +extern volatile pthread_descr __pthread_last_event; + +/* Return the handle corresponding to a thread id */ + +static inline pthread_handle thread_handle(pthread_t id) +{ +  return &__pthread_handles[id % PTHREAD_THREADS_MAX]; +} + +/* Validate a thread handle. Must have acquired h->h_spinlock before. */ + +static inline int invalid_handle(pthread_handle h, pthread_t id) +{ +  return h->h_descr == NULL || h->h_descr->p_tid != id; +} + +/* Fill in defaults left unspecified by pt-machine.h.  */ + +/* The page size we can get from the system.  This should likely not be +   changed by the machine file but, you never know.  */ +#ifndef PAGE_SIZE +#define PAGE_SIZE  (sysconf (_SC_PAGE_SIZE)) +#endif + +/* The max size of the thread stack segments.  If the default +   THREAD_SELF implementation is used, this must be a power of two and +   a multiple of PAGE_SIZE.  */ +#ifndef STACK_SIZE +#define STACK_SIZE  (2 * 1024 * 1024) +#endif + +/* The initial size of the thread stack.  Must be a multiple of PAGE_SIZE.  */ +#ifndef INITIAL_STACK_SIZE +#define INITIAL_STACK_SIZE  (4 * PAGE_SIZE) +#endif + +/* Size of the thread manager stack. The "- 32" avoids wasting space +   with some malloc() implementations. */ +#ifndef THREAD_MANAGER_STACK_SIZE +#define THREAD_MANAGER_STACK_SIZE  (2 * PAGE_SIZE - 32) +#endif + +/* The base of the "array" of thread stacks.  The array will grow down from +   here.  Defaults to the calculated bottom of the initial application +   stack.  */ +#ifndef THREAD_STACK_START_ADDRESS +#define THREAD_STACK_START_ADDRESS  __pthread_initial_thread_bos +#endif + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#ifndef CURRENT_STACK_FRAME +#define CURRENT_STACK_FRAME  ({ char __csf; &__csf; }) +#endif + +/* Recover thread descriptor for the current thread */ + +extern pthread_descr __pthread_find_self (void) __attribute__ ((const)); + +static inline pthread_descr thread_self (void) __attribute__ ((const)); +static inline pthread_descr thread_self (void) +{ +#ifdef THREAD_SELF +  return THREAD_SELF; +#else +  char *sp = CURRENT_STACK_FRAME; +#ifdef __UCLIBC_HAS_MMU__ +  if (sp >= __pthread_initial_thread_bos) +    return &__pthread_initial_thread; +  else if (sp >= __pthread_manager_thread_bos +	   && sp < __pthread_manager_thread_tos) +    return &__pthread_manager_thread; +  else if (__pthread_nonstandard_stacks) +    return __pthread_find_self(); +  else +    return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1; +#else +  /* For non-MMU we need to be more careful about the initial thread stack. +   * We refine the initial thread stack bounds dynamically as we allocate +   * the other stack frame such that it doesn't overlap with them. Then +   * we can be sure to pick the right thread according to the current SP */ + +  /* Since we allow other stack frames to be above or below, we need to +   * treat this case special. When pthread_initialize() wasn't called yet, +   * only the initial thread is there. */ +  if (__pthread_initial_thread_bos == NULL) { +      return &__pthread_initial_thread; +  } +  else if (sp >= __pthread_initial_thread_bos +	   && sp < __pthread_initial_thread_tos) { +      return &__pthread_initial_thread; +  } +  else if (sp >= __pthread_manager_thread_bos +	   && sp < __pthread_manager_thread_tos) { +      return &__pthread_manager_thread; +  } +  else { +      return __pthread_find_self(); +  } +#endif /* __UCLIBC_HAS_MMU__ */ +#endif +} + +/* Max number of times we must spin on a spinlock calling sched_yield(). +   After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */ + +#ifndef MAX_SPIN_COUNT +#define MAX_SPIN_COUNT 50 +#endif + +/* Duration of sleep (in nanoseconds) when we can't acquire a spinlock +   after MAX_SPIN_COUNT iterations of sched_yield(). +   With the 2.0 and 2.1 kernels, this MUST BE > 2ms. +   (Otherwise the kernel does busy-waiting for realtime threads, +    giving other threads no chance to run.) */ + +#ifndef SPIN_SLEEP_DURATION +#define SPIN_SLEEP_DURATION 2000001 +#endif + +/* Debugging */ + +#ifdef DEBUG +#include <assert.h> +#define ASSERT assert +#define MSG __pthread_message +#else +#define ASSERT(x) +#define MSG(msg,arg...) +#endif + +/* Internal global functions */ + +void __pthread_destroy_specifics(void); +void __pthread_perform_cleanup(void); +int __pthread_initialize_manager(void); +void __pthread_message(char * fmt, ...); +int __pthread_manager(void *reqfd); +int __pthread_manager_event(void *reqfd); +void __pthread_manager_sighandler(int sig); +void __pthread_reset_main_thread(void); +void __fresetlockfiles(void); +void __pthread_manager_adjust_prio(int thread_prio); +void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif); + +extern int __pthread_attr_setguardsize __P ((pthread_attr_t *__attr, +					     size_t __guardsize)); +extern int __pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr, +					     size_t *__guardsize)); +extern int __pthread_attr_setstackaddr __P ((pthread_attr_t *__attr, +					     void *__stackaddr)); +extern int __pthread_attr_getstackaddr __P ((__const pthread_attr_t *__attr, +					     void **__stackaddr)); +extern int __pthread_attr_setstacksize __P ((pthread_attr_t *__attr, +					     size_t __stacksize)); +extern int __pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr, +					     size_t *__stacksize)); +extern int __pthread_getconcurrency __P ((void)); +extern int __pthread_setconcurrency __P ((int __level)); +extern int __pthread_mutexattr_gettype __P ((__const pthread_mutexattr_t *__attr, +					     int *__kind)); +extern void __pthread_kill_other_threads_np __P ((void)); + +void __pthread_restart_old(pthread_descr th); +void __pthread_suspend_old(pthread_descr self); + +void __pthread_restart_new(pthread_descr th); +void __pthread_suspend_new(pthread_descr self); + +void __pthread_wait_for_restart_signal(pthread_descr self); + +void __pthread_init_condvar(int rt_sig_available); + +/* Global pointers to old or new suspend functions */ + +extern void (*__pthread_restart)(pthread_descr); +extern void (*__pthread_suspend)(pthread_descr); + +/* Prototypes for the function without cancelation support when the +   normal version has it.  */ +extern int __libc_close (int fd); +extern int __libc_nanosleep (const struct timespec *requested_time, +			     struct timespec *remaining); +extern ssize_t __libc_read (int fd, void *buf, size_t count); +extern pid_t __libc_waitpid (pid_t pid, int *stat_loc, int options); +extern ssize_t __libc_write (int fd, const void *buf, size_t count); + +/* Prototypes for some of the new semaphore functions.  */ +extern int __new_sem_post (sem_t * sem); + +/* The functions called the signal events.  */ +extern void __linuxthreads_create_event (void); +extern void __linuxthreads_death_event (void); +extern void __linuxthreads_reap_event (void); + +#endif /* internals.h */ diff --git a/libpthread/linuxthreads/join.c b/libpthread/linuxthreads/join.c new file mode 100644 index 000000000..ccb11b124 --- /dev/null +++ b/libpthread/linuxthreads/join.c @@ -0,0 +1,213 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Thread termination and joining */ + +#include <errno.h> +#include <sched.h> +#include <unistd.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" +#include "debug.h" /* PDEBUG, added by StS */ + +void pthread_exit(void * retval) +{ +  pthread_descr self = thread_self(); +  pthread_descr joining; +  struct pthread_request request; +PDEBUG("self=%p, pid=%d\n", self, self->p_pid); + +  /* Reset the cancellation flag to avoid looping if the cleanup handlers +     contain cancellation points */ +  THREAD_SETMEM(self, p_canceled, 0); +  /* Call cleanup functions and destroy the thread-specific data */ +  __pthread_perform_cleanup(); +  __pthread_destroy_specifics(); +  /* Store return value */ +  __pthread_lock(THREAD_GETMEM(self, p_lock), self); +  THREAD_SETMEM(self, p_retval, retval); +  /* Say that we've terminated */ +  THREAD_SETMEM(self, p_terminated, 1); +  /* See whether we have to signal the death.  */ +  if (THREAD_GETMEM(self, p_report_events)) +    { +      /* See whether TD_DEATH is in any of the mask.  */ +      int idx = __td_eventword (TD_DEATH); +      uint32_t mask = __td_eventmask (TD_DEATH); + +      if ((mask & (__pthread_threads_events.event_bits[idx] +		   | THREAD_GETMEM(self, +				   p_eventbuf.eventmask).event_bits[idx])) +	  != 0) +	{ +	  /* Yep, we have to signal the death.  */ +	  THREAD_SETMEM(self, p_eventbuf.eventnum, TD_DEATH); +	  THREAD_SETMEM(self, p_eventbuf.eventdata, self); +	  __pthread_last_event = self; + +	  /* Now call the function to signal the event.  */ +	  __linuxthreads_death_event(); +	} +    } +  /* See if someone is joining on us */ +  joining = THREAD_GETMEM(self, p_joining); +PDEBUG("joining = %p, pid=%d\n", joining, joining->p_pid); +  __pthread_unlock(THREAD_GETMEM(self, p_lock)); +  /* Restart joining thread if any */ +  if (joining != NULL) restart(joining); +  /* If this is the initial thread, block until all threads have terminated. +     If another thread calls exit, we'll be terminated from our signal +     handler. */ +  if (self == __pthread_main_thread && __pthread_manager_request >= 0) { +    request.req_thread = self; +    request.req_kind = REQ_MAIN_THREAD_EXIT; +    __libc_write(__pthread_manager_request, (char *)&request, sizeof(request)); +    suspend(self); +  } +  /* Exit the process (but don't flush stdio streams, and don't run +     atexit functions). */ +  _exit(0); +} + +/* Function called by pthread_cancel to remove the thread from +   waiting on a condition variable queue. */ + +static int join_extricate_func(void *obj, pthread_descr th) +{ +  volatile pthread_descr self = thread_self(); +  pthread_handle handle = obj; +  pthread_descr jo; +  int did_remove = 0; + +  __pthread_lock(&handle->h_lock, self); +  jo = handle->h_descr; +  did_remove = jo->p_joining != NULL; +  jo->p_joining = NULL; +  __pthread_unlock(&handle->h_lock); + +  return did_remove; +} + +int pthread_join(pthread_t thread_id, void ** thread_return) +{ +  volatile pthread_descr self = thread_self(); +  struct pthread_request request; +  pthread_handle handle = thread_handle(thread_id); +  pthread_descr th; +  pthread_extricate_if extr; +  int already_canceled = 0; +PDEBUG("\n"); + +  /* Set up extrication interface */ +  extr.pu_object = handle; +  extr.pu_extricate_func = join_extricate_func; + +  __pthread_lock(&handle->h_lock, self); +  if (invalid_handle(handle, thread_id)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } +  th = handle->h_descr; +  if (th == self) { +    __pthread_unlock(&handle->h_lock); +    return EDEADLK; +  } +  /* If detached or already joined, error */ +  if (th->p_detached || th->p_joining != NULL) { +    __pthread_unlock(&handle->h_lock); +    return EINVAL; +  } +  /* If not terminated yet, suspend ourselves. */ +  if (! th->p_terminated) { +    /* Register extrication interface */ +    __pthread_set_own_extricate_if(self, &extr);  +    if (!(THREAD_GETMEM(self, p_canceled) +	&& THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) +      th->p_joining = self; +    else +      already_canceled = 1; +    __pthread_unlock(&handle->h_lock); + +    if (already_canceled) { +      __pthread_set_own_extricate_if(self, 0);  +      pthread_exit(PTHREAD_CANCELED); +    } + +PDEBUG("before suspend\n"); +    suspend(self); +PDEBUG("after suspend\n"); +    /* Deregister extrication interface */ +    __pthread_set_own_extricate_if(self, 0);  + +    /* This is a cancellation point */ +    if (THREAD_GETMEM(self, p_woken_by_cancel) +	&& THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +      THREAD_SETMEM(self, p_woken_by_cancel, 0); +      pthread_exit(PTHREAD_CANCELED); +    } +    __pthread_lock(&handle->h_lock, self); +  } +  /* Get return value */ +  if (thread_return != NULL) *thread_return = th->p_retval; +  __pthread_unlock(&handle->h_lock); +  /* Send notification to thread manager */ +  if (__pthread_manager_request >= 0) { +    request.req_thread = self; +    request.req_kind = REQ_FREE; +    request.req_args.free.thread_id = thread_id; +    __libc_write(__pthread_manager_request, +		 (char *) &request, sizeof(request)); +  } +  return 0; +} + +int pthread_detach(pthread_t thread_id) +{ +  int terminated; +  struct pthread_request request; +  pthread_handle handle = thread_handle(thread_id); +  pthread_descr th; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, thread_id)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } +  th = handle->h_descr; +  /* If already detached, error */ +  if (th->p_detached) { +    __pthread_unlock(&handle->h_lock); +    return EINVAL; +  } +  /* If already joining, don't do anything. */ +  if (th->p_joining != NULL) { +    __pthread_unlock(&handle->h_lock); +    return 0; +  } +  /* Mark as detached */ +  th->p_detached = 1; +  terminated = th->p_terminated; +  __pthread_unlock(&handle->h_lock); +  /* If already terminated, notify thread manager to reclaim resources */ +  if (terminated && __pthread_manager_request >= 0) { +    request.req_thread = thread_self(); +    request.req_kind = REQ_FREE; +    request.req_args.free.thread_id = thread_id; +    __libc_write(__pthread_manager_request, +		 (char *) &request, sizeof(request)); +  } +  return 0; +} diff --git a/libpthread/linuxthreads/linuxthreads.texi b/libpthread/linuxthreads/linuxthreads.texi new file mode 100644 index 000000000..7a98103b3 --- /dev/null +++ b/libpthread/linuxthreads/linuxthreads.texi @@ -0,0 +1,1428 @@ +@node POSIX Threads +@c @node POSIX Threads, , Top, Top +@chapter POSIX Threads +@c %MENU% The standard threads library + +@c This chapter needs more work bigtime. -zw + +This chapter describes the pthreads (POSIX threads) library.  This +library provides support functions for multithreaded programs: thread +primitives, synchronization objects, and so forth.  It also implements +POSIX 1003.1b semaphores (not to be confused with System V semaphores). + +The threads operations (@samp{pthread_*}) do not use @var{errno}. +Instead they return an error code directly.  The semaphore operations do +use @var{errno}. + +@menu +* Basic Thread Operations::     Creating, terminating, and waiting for threads. +* Thread Attributes::           Tuning thread scheduling. +* Cancellation::                Stopping a thread before it's done. +* Cleanup Handlers::            Deallocating resources when a thread is +                                  cancelled. +* Mutexes::                     One way to synchronize threads. +* Condition Variables::         Another way. +* POSIX Semaphores::            And a third way. +* Thread-Specific Data::        Variables with different values in +                                  different threads. +* Threads and Signal Handling:: Why you should avoid mixing the two, and +                                  how to do it if you must. +* Miscellaneous Thread Functions:: A grab bag of utility routines. +@end menu + +@node Basic Thread Operations +@section Basic Thread Operations + +These functions are the thread equivalents of @code{fork}, @code{exit}, +and @code{wait}. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_create (pthread_t * @var{thread}, pthread_attr_t * @var{attr}, void * (*@var{start_routine})(void *), void * @var{arg}) +@code{pthread_create} creates a new thread of control that executes +concurrently with the calling thread. The new thread calls the +function @var{start_routine}, passing it @var{arg} as first argument. The +new thread terminates either explicitly, by calling @code{pthread_exit}, +or implicitly, by returning from the @var{start_routine} function. The +latter case is equivalent to calling @code{pthread_exit} with the result +returned by @var{start_routine} as exit code. + +The @var{attr} argument specifies thread attributes to be applied to the +new thread. @xref{Thread Attributes}, for details. The @var{attr} +argument can also be @code{NULL}, in which case default attributes are +used: the created thread is joinable (not detached) and has an ordinary +(not realtime) scheduling policy. + +On success, the identifier of the newly created thread is stored in the +location pointed by the @var{thread} argument, and a 0 is returned. On +error, a non-zero error code is returned. + +This function may return the following errors: +@table @code +@item EAGAIN +Not enough system resources to create a process for the new thread, +or more than @code{PTHREAD_THREADS_MAX} threads are already active. +@end table +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun void pthread_exit (void *@var{retval}) +@code{pthread_exit} terminates the execution of the calling thread.  All +cleanup handlers (@pxref{Cleanup Handlers}) that have been set for the +calling thread with @code{pthread_cleanup_push} are executed in reverse +order (the most recently pushed handler is executed first). Finalization +functions for thread-specific data are then called for all keys that +have non-@code{NULL} values associated with them in the calling thread +(@pxref{Thread-Specific Data}).  Finally, execution of the calling +thread is stopped. + +The @var{retval} argument is the return value of the thread. It can be +retrieved from another thread using @code{pthread_join}. + +The @code{pthread_exit} function never returns. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cancel (pthread_t @var{thread}) + +@code{pthread_cancel} sends a cancellation request to the thread denoted +by the @var{thread} argument.  If there is no such thread, +@code{pthread_cancel} fails and returns @code{ESRCH}.  Otherwise it +returns 0. @xref{Cancellation}, for details. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_join (pthread_t @var{th}, void **thread_@var{return}) +@code{pthread_join} suspends the execution of the calling thread until +the thread identified by @var{th} terminates, either by calling +@code{pthread_exit} or by being cancelled. + +If @var{thread_return} is not @code{NULL}, the return value of @var{th} +is stored in the location pointed to by @var{thread_return}.  The return +value of @var{th} is either the argument it gave to @code{pthread_exit}, +or @code{PTHREAD_CANCELED} if @var{th} was cancelled. + +The joined thread @code{th} must be in the joinable state: it must not +have been detached using @code{pthread_detach} or the +@code{PTHREAD_CREATE_DETACHED} attribute to @code{pthread_create}. + +When a joinable thread terminates, its memory resources (thread +descriptor and stack) are not deallocated until another thread performs +@code{pthread_join} on it. Therefore, @code{pthread_join} must be called +once for each joinable thread created to avoid memory leaks. + +At most one thread can wait for the termination of a given +thread. Calling @code{pthread_join} on a thread @var{th} on which +another thread is already waiting for termination returns an error. + +@code{pthread_join} is a cancellation point. If a thread is canceled +while suspended in @code{pthread_join}, the thread execution resumes +immediately and the cancellation is executed without waiting for the +@var{th} thread to terminate. If cancellation occurs during +@code{pthread_join}, the @var{th} thread remains not joined. + +On success, the return value of @var{th} is stored in the location +pointed to by @var{thread_return}, and 0 is returned. On error, one of +the following values is returned: +@table @code +@item ESRCH +No thread could be found corresponding to that specified by @var{th}. +@item EINVAL +The @var{th} thread has been detached, or another thread is already +waiting on termination of @var{th}. +@item EDEADLK +The @var{th} argument refers to the calling thread. +@end table +@end deftypefun + +@node Thread Attributes +@section Thread Attributes + +@comment pthread.h +@comment POSIX + +Threads have a number of attributes that may be set at creation time. +This is done by filling a thread attribute object @var{attr} of type +@code{pthread_attr_t}, then passing it as second argument to +@code{pthread_create}. Passing @code{NULL} is equivalent to passing a +thread attribute object with all attributes set to their default values. + +Attribute objects are consulted only when creating a new thread.  The +same attribute object can be used for creating several threads. +Modifying an attribute object after a call to @code{pthread_create} does +not change the attributes of the thread previously created. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_attr_init (pthread_attr_t *@var{attr}) +@code{pthread_attr_init} initializes the thread attribute object +@var{attr} and fills it with default values for the attributes. (The +default values are listed below for each attribute.) + +Each attribute @var{attrname} (see below for a list of all attributes) +can be individually set using the function +@code{pthread_attr_set@var{attrname}} and retrieved using the function +@code{pthread_attr_get@var{attrname}}. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_attr_destroy (pthread_attr_t *@var{attr}) +@code{pthread_attr_destroy} destroys the attribute object pointed to by +@var{attr} releasing any resources associated with it.  @var{attr} is +left in an undefined state, and you must not use it again in a call to +any pthreads function until it has been reinitialized. +@end deftypefun + +@findex pthread_attr_setinheritsched +@findex pthread_attr_setschedparam +@findex pthread_attr_setschedpolicy +@findex pthread_attr_setscope +@comment pthread.h +@comment POSIX +@deftypefun int pthread_attr_set@var{attr} (pthread_attr_t *@var{obj}, int @var{value}) +Set attribute @var{attr} to @var{value} in the attribute object pointed +to by @var{obj}.  See below for a list of possible attributes and the +values they can take. + +On success, these functions return 0.  If @var{value} is not meaningful +for the @var{attr} being modified, they will return the error code +@code{EINVAL}.  Some of the functions have other failure modes; see +below. +@end deftypefun + +@findex pthread_attr_getinheritsched +@findex pthread_attr_getschedparam +@findex pthread_attr_getschedpolicy +@findex pthread_attr_getscope +@comment pthread.h +@comment POSIX +@deftypefun int pthread_attr_get@var{attr} (const pthread_attr_t *@var{obj}, int *@var{value}) +Store the current setting of @var{attr} in @var{obj} into the variable +pointed to by @var{value}. + +These functions always return 0. +@end deftypefun + +The following thread attributes are supported: +@table @samp +@item detachstate +Choose whether the thread is created in the joinable state (value +@code{PTHREAD_CREATE_JOINABLE}) or in the detached state +(@code{PTHREAD_CREATE_DETACHED}).  The default is +@code{PTHREAD_CREATE_JOINABLE}. + +In the joinable state, another thread can synchronize on the thread +termination and recover its termination code using @code{pthread_join}, +but some of the thread resources are kept allocated after the thread +terminates, and reclaimed only when another thread performs +@code{pthread_join} on that thread. + +In the detached state, the thread resources are immediately freed when +it terminates, but @code{pthread_join} cannot be used to synchronize on +the thread termination. + +A thread created in the joinable state can later be put in the detached +thread using @code{pthread_detach}. + +@item schedpolicy +Select the scheduling policy for the thread: one of @code{SCHED_OTHER} +(regular, non-realtime scheduling), @code{SCHED_RR} (realtime, +round-robin) or @code{SCHED_FIFO} (realtime, first-in first-out). +The default is @code{SCHED_OTHER}. +@c Not doc'd in our manual: FIXME. +@c See @code{sched_setpolicy} for more information on scheduling policies. + +The realtime scheduling policies @code{SCHED_RR} and @code{SCHED_FIFO} +are available only to processes with superuser privileges. +@code{pthread_attr_setschedparam} will fail and return @code{ENOTSUP} if +you try to set a realtime policy when you are unprivileged. + +The scheduling policy of a thread can be changed after creation with +@code{pthread_setschedparam}. + +@item schedparam +Change the scheduling parameter (the scheduling priority) +for the thread.  The default is 0. + +This attribute is not significant if the scheduling policy is +@code{SCHED_OTHER}; it only matters for the realtime policies +@code{SCHED_RR} and @code{SCHED_FIFO}. + +The scheduling priority of a thread can be changed after creation with +@code{pthread_setschedparam}. + +@item inheritsched +Choose whether the scheduling policy and scheduling parameter for the +newly created thread are determined by the values of the +@var{schedpolicy} and @var{schedparam} attributes (value +@code{PTHREAD_EXPLICIT_SCHED}) or are inherited from the parent thread +(value @code{PTHREAD_INHERIT_SCHED}).  The default is +@code{PTHREAD_EXPLICIT_SCHED}. + +@item scope +Choose the scheduling contention scope for the created thread.  The +default is @code{PTHREAD_SCOPE_SYSTEM}, meaning that the threads contend +for CPU time with all processes running on the machine. In particular, +thread priorities are interpreted relative to the priorities of all +other processes on the machine. The other possibility, +@code{PTHREAD_SCOPE_PROCESS}, means that scheduling contention occurs +only between the threads of the running process: thread priorities are +interpreted relative to the priorities of the other threads of the +process, regardless of the priorities of other processes. + +@code{PTHREAD_SCOPE_PROCESS} is not supported in LinuxThreads.  If you +try to set the scope to this value @code{pthread_attr_setscope} will +fail and return @code{ENOTSUP}. +@end table + +@node Cancellation +@section Cancellation + +Cancellation is the mechanism by which a thread can terminate the +execution of another thread. More precisely, a thread can send a +cancellation request to another thread. Depending on its settings, the +target thread can then either ignore the request, honor it immediately, +or defer it till it reaches a cancellation point.  When threads are +first created by @code{pthread_create}, they always defer cancellation +requests. + +When a thread eventually honors a cancellation request, it behaves as if +@code{pthread_exit(PTHREAD_CANCELED)} was called.  All cleanup handlers +are executed in reverse order, finalization functions for +thread-specific data are called, and finally the thread stops executing. +If the cancelled thread was joinable, the return value +@code{PTHREAD_CANCELED} is provided to whichever thread calls +@var{pthread_join} on it. See @code{pthread_exit} for more information. + +Cancellation points are the points where the thread checks for pending +cancellation requests and performs them.  The POSIX threads functions +@code{pthread_join}, @code{pthread_cond_wait}, +@code{pthread_cond_timedwait}, @code{pthread_testcancel}, +@code{sem_wait}, and @code{sigwait} are cancellation points.  In +addition, these system calls are cancellation points: + +@multitable @columnfractions .33 .33 .33 +@item @t{accept}	@tab @t{open}		@tab @t{sendmsg} +@item @t{close}		@tab @t{pause}		@tab @t{sendto} +@item @t{connect}	@tab @t{read}		@tab @t{system} +@item @t{fcntl}		@tab @t{recv}		@tab @t{tcdrain} +@item @t{fsync}		@tab @t{recvfrom}	@tab @t{wait} +@item @t{lseek}		@tab @t{recvmsg}	@tab @t{waitpid} +@item @t{msync}		@tab @t{send}		@tab @t{write} +@item @t{nanosleep} +@end multitable + +@noindent +All library functions that call these functions (such as +@code{printf}) are also cancellation points. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_setcancelstate (int @var{state}, int *@var{oldstate}) +@code{pthread_setcancelstate} changes the cancellation state for the +calling thread -- that is, whether cancellation requests are ignored or +not. The @var{state} argument is the new cancellation state: either +@code{PTHREAD_CANCEL_ENABLE} to enable cancellation, or +@code{PTHREAD_CANCEL_DISABLE} to disable cancellation (cancellation +requests are ignored). + +If @var{oldstate} is not @code{NULL}, the previous cancellation state is +stored in the location pointed to by @var{oldstate}, and can thus be +restored later by another call to @code{pthread_setcancelstate}. + +If the @var{state} argument is not @code{PTHREAD_CANCEL_ENABLE} or +@code{PTHREAD_CANCEL_DISABLE}, @code{pthread_setcancelstate} fails and +returns @code{EINVAL}.  Otherwise it returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_setcanceltype (int @var{type}, int *@var{oldtype}) +@code{pthread_setcanceltype} changes the type of responses to +cancellation requests for the calling thread: asynchronous (immediate) +or deferred.  The @var{type} argument is the new cancellation type: +either @code{PTHREAD_CANCEL_ASYNCHRONOUS} to cancel the calling thread +as soon as the cancellation request is received, or +@code{PTHREAD_CANCEL_DEFERRED} to keep the cancellation request pending +until the next cancellation point. If @var{oldtype} is not @code{NULL}, +the previous cancellation state is stored in the location pointed to by +@var{oldtype}, and can thus be restored later by another call to +@code{pthread_setcanceltype}. + +If the @var{type} argument is not @code{PTHREAD_CANCEL_DEFERRED} or +@code{PTHREAD_CANCEL_ASYNCHRONOUS}, @code{pthread_setcanceltype} fails +and returns @code{EINVAL}.  Otherwise it returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun void pthread_testcancel (@var{void}) +@code{pthread_testcancel} does nothing except testing for pending +cancellation and executing it. Its purpose is to introduce explicit +checks for cancellation in long sequences of code that do not call +cancellation point functions otherwise. +@end deftypefun + +@node Cleanup Handlers +@section Cleanup Handlers + +Cleanup handlers are functions that get called when a thread terminates, +either by calling @code{pthread_exit} or because of +cancellation. Cleanup handlers are installed and removed following a +stack-like discipline. + +The purpose of cleanup handlers is to free the resources that a thread +may hold at the time it terminates. In particular, if a thread exits or +is cancelled while it owns a locked mutex, the mutex will remain locked +forever and prevent other threads from executing normally. The best way +to avoid this is, just before locking the mutex, to install a cleanup +handler whose effect is to unlock the mutex. Cleanup handlers can be +used similarly to free blocks allocated with @code{malloc} or close file +descriptors on thread termination. + +Here is how to lock a mutex @var{mut} in such a way that it will be +unlocked if the thread is canceled while @var{mut} is locked: + +@smallexample +pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut); +pthread_mutex_lock(&mut); +/* do some work */ +pthread_mutex_unlock(&mut); +pthread_cleanup_pop(0); +@end smallexample + +Equivalently, the last two lines can be replaced by + +@smallexample +pthread_cleanup_pop(1); +@end smallexample + +Notice that the code above is safe only in deferred cancellation mode +(see @code{pthread_setcanceltype}). In asynchronous cancellation mode, a +cancellation can occur between @code{pthread_cleanup_push} and +@code{pthread_mutex_lock}, or between @code{pthread_mutex_unlock} and +@code{pthread_cleanup_pop}, resulting in both cases in the thread trying +to unlock a mutex not locked by the current thread. This is the main +reason why asynchronous cancellation is difficult to use. + +If the code above must also work in asynchronous cancellation mode, +then it must switch to deferred mode for locking and unlocking the +mutex: + +@smallexample +pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); +pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut); +pthread_mutex_lock(&mut); +/* do some work */ +pthread_cleanup_pop(1); +pthread_setcanceltype(oldtype, NULL); +@end smallexample + +The code above can be rewritten in a more compact and efficient way, +using the non-portable functions @code{pthread_cleanup_push_defer_np} +and @code{pthread_cleanup_pop_restore_np}: + +@smallexample +pthread_cleanup_push_defer_np(pthread_mutex_unlock, (void *) &mut); +pthread_mutex_lock(&mut); +/* do some work */ +pthread_cleanup_pop_restore_np(1); +@end smallexample + +@comment pthread.h +@comment POSIX +@deftypefun void pthread_cleanup_push (void (*@var{routine}) (void *), void *@var{arg}) + +@code{pthread_cleanup_push} installs the @var{routine} function with +argument @var{arg} as a cleanup handler. From this point on to the +matching @code{pthread_cleanup_pop}, the function @var{routine} will be +called with arguments @var{arg} when the thread terminates, either +through @code{pthread_exit} or by cancellation. If several cleanup +handlers are active at that point, they are called in LIFO order: the +most recently installed handler is called first. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun void pthread_cleanup_pop (int @var{execute}) +@code{pthread_cleanup_pop} removes the most recently installed cleanup +handler. If the @var{execute} argument is not 0, it also executes the +handler, by calling the @var{routine} function with arguments +@var{arg}. If the @var{execute} argument is 0, the handler is only +removed but not executed. +@end deftypefun + +Matching pairs of @code{pthread_cleanup_push} and +@code{pthread_cleanup_pop} must occur in the same function, at the same +level of block nesting.  Actually, @code{pthread_cleanup_push} and +@code{pthread_cleanup_pop} are macros, and the expansion of +@code{pthread_cleanup_push} introduces an open brace @code{@{} with the +matching closing brace @code{@}} being introduced by the expansion of the +matching @code{pthread_cleanup_pop}. + +@comment pthread.h +@comment GNU +@deftypefun void pthread_cleanup_push_defer_np (void (*@var{routine}) (void *), void *@var{arg}) +@code{pthread_cleanup_push_defer_np} is a non-portable extension that +combines @code{pthread_cleanup_push} and @code{pthread_setcanceltype}. +It pushes a cleanup handler just as @code{pthread_cleanup_push} does, +but also saves the current cancellation type and sets it to deferred +cancellation. This ensures that the cleanup mechanism is effective even +if the thread was initially in asynchronous cancellation mode. +@end deftypefun + +@comment pthread.h +@comment GNU +@deftypefun void pthread_cleanup_pop_restore_np (int @var{execute}) +@code{pthread_cleanup_pop_restore_np} pops a cleanup handler introduced +by @code{pthread_cleanup_push_defer_np}, and restores the cancellation +type to its value at the time @code{pthread_cleanup_push_defer_np} was +called. +@end deftypefun + +@code{pthread_cleanup_push_defer_np} and +@code{pthread_cleanup_pop_restore_np} must occur in matching pairs, at +the same level of block nesting. + +The sequence + +@smallexample +pthread_cleanup_push_defer_np(routine, arg); +... +pthread_cleanup_pop_defer_np(execute); +@end smallexample + +@noindent +is functionally equivalent to (but more compact and efficient than) + +@smallexample +@{ +  int oldtype; +  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); +  pthread_cleanup_push(routine, arg); +  ... +  pthread_cleanup_pop(execute); +  pthread_setcanceltype(oldtype, NULL); +@} +@end smallexample + + +@node Mutexes +@section Mutexes + +A mutex is a MUTual EXclusion device, and is useful for protecting +shared data structures from concurrent modifications, and implementing +critical sections and monitors. + +A mutex has two possible states: unlocked (not owned by any thread), +and locked (owned by one thread). A mutex can never be owned by two +different threads simultaneously. A thread attempting to lock a mutex +that is already locked by another thread is suspended until the owning +thread unlocks the mutex first. + +None of the mutex functions is a cancellation point, not even +@code{pthread_mutex_lock}, in spite of the fact that it can suspend a +thread for arbitrary durations. This way, the status of mutexes at +cancellation points is predictable, allowing cancellation handlers to +unlock precisely those mutexes that need to be unlocked before the +thread stops executing. Consequently, threads using deferred +cancellation should never hold a mutex for extended periods of time. + +It is not safe to call mutex functions from a signal handler.  In +particular, calling @code{pthread_mutex_lock} or +@code{pthread_mutex_unlock} from a signal handler may deadlock the +calling thread. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_init (pthread_mutex_t *@var{mutex}, const pthread_mutexattr_t *@var{mutexattr}) + +@code{pthread_mutex_init} initializes the mutex object pointed to by +@var{mutex} according to the mutex attributes specified in @var{mutexattr}. +If @var{mutexattr} is @code{NULL}, default attributes are used instead. + +The LinuxThreads implementation supports only one mutex attribute, +the @var{mutex kind}, which is either ``fast'', ``recursive'', or +``error checking''. The kind of a mutex determines whether +it can be locked again by a thread that already owns it. +The default kind is ``fast''. + +Variables of type @code{pthread_mutex_t} can also be initialized +statically, using the constants @code{PTHREAD_MUTEX_INITIALIZER} (for +fast mutexes), @code{PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP} (for +recursive mutexes), and @code{PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP} +(for error checking mutexes). + +@code{pthread_mutex_init} always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_lock (pthread_mutex_t *mutex)) +@code{pthread_mutex_lock} locks the given mutex. If the mutex is +currently unlocked, it becomes locked and owned by the calling thread, +and @code{pthread_mutex_lock} returns immediately. If the mutex is +already locked by another thread, @code{pthread_mutex_lock} suspends the +calling thread until the mutex is unlocked. + +If the mutex is already locked by the calling thread, the behavior of +@code{pthread_mutex_lock} depends on the kind of the mutex. If the mutex +is of the ``fast'' kind, the calling thread is suspended.  It will +remain suspended forever, because no other thread can unlock the mutex. +If  the mutex is of the ``error checking'' kind, @code{pthread_mutex_lock} +returns immediately with the error code @code{EDEADLK}.  If the mutex is +of the ``recursive'' kind, @code{pthread_mutex_lock} succeeds and +returns immediately, recording the number of times the calling thread +has locked the mutex. An equal number of @code{pthread_mutex_unlock} +operations must be performed before the mutex returns to the unlocked +state. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_trylock (pthread_mutex_t *@var{mutex}) +@code{pthread_mutex_trylock} behaves identically to +@code{pthread_mutex_lock}, except that it does not block the calling +thread if the mutex is already locked by another thread (or by the +calling thread in the case of a ``fast'' mutex). Instead, +@code{pthread_mutex_trylock} returns immediately with the error code +@code{EBUSY}. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_unlock (pthread_mutex_t *@var{mutex}) +@code{pthread_mutex_unlock} unlocks the given mutex. The mutex is +assumed to be locked and owned by the calling thread on entrance to +@code{pthread_mutex_unlock}. If the mutex is of the ``fast'' kind, +@code{pthread_mutex_unlock} always returns it to the unlocked state. If +it is of the ``recursive'' kind, it decrements the locking count of the +mutex (number of @code{pthread_mutex_lock} operations performed on it by +the calling thread), and only when this count reaches zero is the mutex +actually unlocked. + +On ``error checking'' mutexes, @code{pthread_mutex_unlock} actually +checks at run-time that the mutex is locked on entrance, and that it was +locked by the same thread that is now calling +@code{pthread_mutex_unlock}.  If these conditions are not met, +@code{pthread_mutex_unlock} returns @code{EPERM}, and the mutex remains +unchanged.  ``Fast'' and ``recursive'' mutexes perform no such checks, +thus allowing a locked mutex to be unlocked by a thread other than its +owner. This is non-portable behavior and must not be relied upon. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_destroy (pthread_mutex_t *@var{mutex}) +@code{pthread_mutex_destroy} destroys a mutex object, freeing the +resources it might hold. The mutex must be unlocked on entrance. In the +LinuxThreads implementation, no resources are associated with mutex +objects, thus @code{pthread_mutex_destroy} actually does nothing except +checking that the mutex is unlocked. + +If the mutex is locked by some thread, @code{pthread_mutex_destroy} +returns @code{EBUSY}.  Otherwise it returns 0. +@end deftypefun + +If any of the above functions (except @code{pthread_mutex_init}) +is applied to an uninitialized mutex, they will simply return +@code{EINVAL} and do nothing. + +A shared global variable @var{x} can be protected by a mutex as follows: + +@smallexample +int x; +pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; +@end smallexample + +All accesses and modifications to @var{x} should be bracketed by calls to +@code{pthread_mutex_lock} and @code{pthread_mutex_unlock} as follows: + +@smallexample +pthread_mutex_lock(&mut); +/* operate on x */ +pthread_mutex_unlock(&mut); +@end smallexample + +Mutex attributes can be specified at mutex creation time, by passing a +mutex attribute object as second argument to @code{pthread_mutex_init}. +Passing @code{NULL} is equivalent to passing a mutex attribute object +with all attributes set to their default values. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutexattr_init (pthread_mutexattr_t *@var{attr}) +@code{pthread_mutexattr_init} initializes the mutex attribute object +@var{attr} and fills it with default values for the attributes. + +This function always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutexattr_destroy (pthread_mutexattr_t *@var{attr}) +@code{pthread_mutexattr_destroy} destroys a mutex attribute object, +which must not be reused until it is +reinitialized. @code{pthread_mutexattr_destroy} does nothing in the +LinuxThreads implementation. + +This function always returns 0. +@end deftypefun + +LinuxThreads supports only one mutex attribute: the mutex kind, which is +either @code{PTHREAD_MUTEX_FAST_NP} for ``fast'' mutexes, +@code{PTHREAD_MUTEX_RECURSIVE_NP} for ``recursive'' mutexes, or +@code{PTHREAD_MUTEX_ERRORCHECK_NP} for ``error checking'' mutexes.  As +the @code{NP} suffix indicates, this is a non-portable extension to the +POSIX standard and should not be employed in portable programs. + +The mutex kind determines what happens if a thread attempts to lock a +mutex it already owns with @code{pthread_mutex_lock}. If the mutex is of +the ``fast'' kind, @code{pthread_mutex_lock} simply suspends the calling +thread forever.  If the mutex is of the ``error checking'' kind, +@code{pthread_mutex_lock} returns immediately with the error code +@code{EDEADLK}.  If the mutex is of the ``recursive'' kind, the call to +@code{pthread_mutex_lock} returns immediately with a success return +code. The number of times the thread owning the mutex has locked it is +recorded in the mutex. The owning thread must call +@code{pthread_mutex_unlock} the same number of times before the mutex +returns to the unlocked state. + +The default mutex kind is ``fast'', that is, @code{PTHREAD_MUTEX_FAST_NP}. + +@comment pthread.h +@comment GNU +@deftypefun int pthread_mutexattr_setkind_np (pthread_mutexattr_t *@var{attr}, int @var{kind}) +@code{pthread_mutexattr_setkind_np} sets the mutex kind attribute in +@var{attr} to the value specified by @var{kind}. + +If @var{kind} is not @code{PTHREAD_MUTEX_FAST_NP}, +@code{PTHREAD_MUTEX_RECURSIVE_NP}, or +@code{PTHREAD_MUTEX_ERRORCHECK_NP}, this function will return +@code{EINVAL} and leave @var{attr} unchanged. +@end deftypefun + +@comment pthread.h +@comment GNU +@deftypefun int pthread_mutexattr_getkind_np (const pthread_mutexattr_t *@var{attr}, int *@var{kind}) +@code{pthread_mutexattr_getkind_np} retrieves the current value of the +mutex kind attribute in @var{attr} and stores it in the location pointed +to by @var{kind}. + +This function always returns 0. +@end deftypefun + +@node Condition Variables +@section Condition Variables + +A condition (short for ``condition variable'') is a synchronization +device that allows threads to suspend execution until some predicate on +shared data is satisfied. The basic operations on conditions are: signal +the condition (when the predicate becomes true), and wait for the +condition, suspending the thread execution until another thread signals +the condition. + +A condition variable must always be associated with a mutex, to avoid +the race condition where a thread prepares to wait on a condition +variable and another thread signals the condition just before the first +thread actually waits on it. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_init (pthread_cond_t *@var{cond}, pthread_condattr_t *cond_@var{attr}) + +@code{pthread_cond_init} initializes the condition variable @var{cond}, +using the condition attributes specified in @var{cond_attr}, or default +attributes if @var{cond_attr} is @code{NULL}. The LinuxThreads +implementation supports no attributes for conditions, hence the +@var{cond_attr} parameter is actually ignored. + +Variables of type @code{pthread_cond_t} can also be initialized +statically, using the constant @code{PTHREAD_COND_INITIALIZER}. + +This function always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_signal (pthread_cond_t *@var{cond}) +@code{pthread_cond_signal} restarts one of the threads that are waiting +on the condition variable @var{cond}. If no threads are waiting on +@var{cond}, nothing happens. If several threads are waiting on +@var{cond}, exactly one is restarted, but it is not specified which. + +This function always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_broadcast (pthread_cond_t *@var{cond}) +@code{pthread_cond_broadcast} restarts all the threads that are waiting +on the condition variable @var{cond}. Nothing happens if no threads are +waiting on @var{cond}. + +This function always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_wait (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex}) +@code{pthread_cond_wait} atomically unlocks the @var{mutex} (as per +@code{pthread_unlock_mutex}) and waits for the condition variable +@var{cond} to be signaled. The thread execution is suspended and does +not consume any CPU time until the condition variable is signaled. The +@var{mutex} must be locked by the calling thread on entrance to +@code{pthread_cond_wait}. Before returning to the calling thread, +@code{pthread_cond_wait} re-acquires @var{mutex} (as per +@code{pthread_lock_mutex}). + +Unlocking the mutex and suspending on the condition variable is done +atomically. Thus, if all threads always acquire the mutex before +signaling the condition, this guarantees that the condition cannot be +signaled (and thus ignored) between the time a thread locks the mutex +and the time it waits on the condition variable. + +This function always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_timedwait (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex}, const struct timespec *@var{abstime}) +@code{pthread_cond_timedwait} atomically unlocks @var{mutex} and waits +on @var{cond}, as @code{pthread_cond_wait} does, but it also bounds the +duration of the wait. If @var{cond} has not been signaled before time +@var{abstime}, the mutex @var{mutex} is re-acquired and +@code{pthread_cond_timedwait} returns the error code @code{ETIMEDOUT}. +The wait can also be interrupted by a signal; in that case +@code{pthread_cond_timedwait} returns @code{EINTR}. + +The @var{abstime} parameter specifies an absolute time, with the same +origin as @code{time} and @code{gettimeofday}: an @var{abstime} of 0 +corresponds to 00:00:00 GMT, January 1, 1970. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_cond_destroy (pthread_cond_t *@var{cond}) +@code{pthread_cond_destroy} destroys the condition variable @var{cond}, +freeing the resources it might hold.  If any threads are waiting on the +condition variable, @code{pthread_cond_destroy} leaves @var{cond} +untouched and returns @code{EBUSY}.  Otherwise it returns 0, and +@var{cond} must not be used again until it is reinitialized. + +In the LinuxThreads implementation, no resources are associated with +condition variables, so @code{pthread_cond_destroy} actually does +nothing. +@end deftypefun + +@code{pthread_cond_wait} and @code{pthread_cond_timedwait} are +cancellation points. If a thread is cancelled while suspended in one of +these functions, the thread immediately resumes execution, relocks the +mutex specified by  @var{mutex}, and finally executes the cancellation. +Consequently, cleanup handlers are assured that @var{mutex} is locked +when they are called. + +It is not safe to call the condition variable functions from a signal +handler. In particular, calling @code{pthread_cond_signal} or +@code{pthread_cond_broadcast} from a signal handler may deadlock the +calling thread. + +Consider two shared variables @var{x} and @var{y}, protected by the +mutex @var{mut}, and a condition variable @var{cond} that is to be +signaled whenever @var{x} becomes greater than @var{y}. + +@smallexample +int x,y; +pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +@end smallexample + +Waiting until @var{x} is greater than @var{y} is performed as follows: + +@smallexample +pthread_mutex_lock(&mut); +while (x <= y) @{ +        pthread_cond_wait(&cond, &mut); +@} +/* operate on x and y */ +pthread_mutex_unlock(&mut); +@end smallexample + +Modifications on @var{x} and @var{y} that may cause @var{x} to become greater than +@var{y} should signal the condition if needed: + +@smallexample +pthread_mutex_lock(&mut); +/* modify x and y */ +if (x > y) pthread_cond_broadcast(&cond); +pthread_mutex_unlock(&mut); +@end smallexample + +If it can be proved that at most one waiting thread needs to be waken +up (for instance, if there are only two threads communicating through +@var{x} and @var{y}), @code{pthread_cond_signal} can be used as a slightly more +efficient alternative to @code{pthread_cond_broadcast}. In doubt, use +@code{pthread_cond_broadcast}. + +To wait for @var{x} to becomes greater than @var{y} with a timeout of 5 +seconds, do: + +@smallexample +struct timeval now; +struct timespec timeout; +int retcode; + +pthread_mutex_lock(&mut); +gettimeofday(&now); +timeout.tv_sec = now.tv_sec + 5; +timeout.tv_nsec = now.tv_usec * 1000; +retcode = 0; +while (x <= y && retcode != ETIMEDOUT) @{ +        retcode = pthread_cond_timedwait(&cond, &mut, &timeout); +@} +if (retcode == ETIMEDOUT) @{ +        /* timeout occurred */ +@} else @{ +        /* operate on x and y */ +@} +pthread_mutex_unlock(&mut); +@end smallexample + +Condition attributes can be specified at condition creation time, by +passing a condition attribute object as second argument to +@code{pthread_cond_init}.  Passing @code{NULL} is equivalent to passing +a condition attribute object with all attributes set to their default +values. + +The LinuxThreads implementation supports no attributes for +conditions. The functions on condition attributes are included only for +compliance with the POSIX standard. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_condattr_init (pthread_condattr_t *@var{attr}) +@deftypefunx int pthread_condattr_destroy (pthread_condattr_t *@var{attr}) +@code{pthread_condattr_init} initializes the condition attribute object +@var{attr} and fills it with default values for the attributes. +@code{pthread_condattr_destroy} destroys the condition attribute object +@var{attr}. + +Both functions do nothing in the LinuxThreads implementation. + +@code{pthread_condattr_init} and @code{pthread_condattr_destroy} always +return 0. +@end deftypefun + +@node POSIX Semaphores +@section POSIX Semaphores + +@vindex SEM_VALUE_MAX +Semaphores are counters for resources shared between threads. The +basic operations on semaphores are: increment the counter atomically, +and wait until the counter is non-null and decrement it atomically. + +Semaphores have a maximum value past which they cannot be incremented. +The macro @code{SEM_VALUE_MAX} is defined to be this maximum value.  In +the GNU C library, @code{SEM_VALUE_MAX} is equal to @code{INT_MAX} +(@pxref{Range of Type}), but it may be much smaller on other systems. + +The pthreads library implements POSIX 1003.1b semaphores.  These should +not be confused with System V semaphores (@code{ipc}, @code{semctl} and +@code{semop}). +@c !!! SysV IPC is not doc'd at all in our manual + +All the semaphore functions and macros are defined in @file{semaphore.h}. + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_init (sem_t *@var{sem}, int @var{pshared}, unsigned int @var{value}) +@code{sem_init} initializes the semaphore object pointed to by +@var{sem}. The count associated with the semaphore is set initially to +@var{value}. The @var{pshared} argument indicates whether the semaphore +is local to the current process (@var{pshared} is zero) or is to be +shared between several processes (@var{pshared} is not zero). + +On success @code{sem_init} returns 0.  On failure it returns -1 and sets +@var{errno} to one of the following values: + +@table @code +@item EINVAL +@var{value} exceeds the maximal counter value @code{SEM_VALUE_MAX} + +@item ENOSYS +@var{pshared} is not zero.  LinuxThreads currently does not support +process-shared semaphores.  (This will eventually change.) +@end table +@end deftypefun + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_destroy (sem_t * @var{sem}) +@code{sem_destroy} destroys a semaphore object, freeing the resources it +might hold.  If any threads are waiting on the semaphore when +@code{sem_destroy} is called, it fails and sets @var{errno} to +@code{EBUSY}. + +In the LinuxThreads implementation, no resources are associated with +semaphore objects, thus @code{sem_destroy} actually does nothing except +checking that no thread is waiting on the semaphore.  This will change +when process-shared semaphores are implemented. +@end deftypefun + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_wait (sem_t * @var{sem}) +@code{sem_wait} suspends the calling thread until the semaphore pointed +to by @var{sem} has non-zero count. It then atomically decreases the +semaphore count. + +@code{sem_wait} is a cancellation point.  It always returns 0. +@end deftypefun + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_trywait (sem_t * @var{sem}) +@code{sem_trywait} is a non-blocking variant of @code{sem_wait}. If the +semaphore pointed to by @var{sem} has non-zero count, the count is +atomically decreased and @code{sem_trywait} immediately returns 0.  If +the semaphore count is zero, @code{sem_trywait} immediately returns -1 +and sets errno to @code{EAGAIN}. +@end deftypefun + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_post (sem_t * @var{sem}) +@code{sem_post} atomically increases the count of the semaphore pointed to +by @var{sem}. This function never blocks. + +@c !!! This para appears not to agree with the code. +On processors supporting atomic compare-and-swap (Intel 486, Pentium and +later, Alpha, PowerPC, MIPS II, Motorola 68k, Ultrasparc), the +@code{sem_post} function is can safely be called from signal handlers. +This is the only thread synchronization function provided by POSIX +threads that is async-signal safe.  On the Intel 386 and earlier Sparc +chips, the current LinuxThreads implementation of @code{sem_post} is not +async-signal safe, because the hardware does not support the required +atomic operations. + +@code{sem_post} always succeeds and returns 0, unless the semaphore +count would exceed @code{SEM_VALUE_MAX} after being incremented.  In +that case @code{sem_post} returns -1 and sets @var{errno} to +@code{EINVAL}.  The semaphore count is left unchanged. +@end deftypefun + +@comment semaphore.h +@comment POSIX +@deftypefun int sem_getvalue (sem_t * @var{sem}, int * @var{sval}) +@code{sem_getvalue} stores in the location pointed to by @var{sval} the +current count of the semaphore @var{sem}.  It always returns 0. +@end deftypefun + +@node Thread-Specific Data +@section Thread-Specific Data + +Programs often need global or static variables that have different +values in different threads. Since threads share one memory space, this +cannot be achieved with regular variables. Thread-specific data is the +POSIX threads answer to this need. + +Each thread possesses a private memory block, the thread-specific data +area, or TSD area for short. This area is indexed by TSD keys. The TSD +area associates values of type @code{void *} to TSD keys. TSD keys are +common to all threads, but the value associated with a given TSD key can +be different in each thread. + +For concreteness, the TSD areas can be viewed as arrays of @code{void *} +pointers, TSD keys as integer indices into these arrays, and the value +of a TSD key as the value of the corresponding array element in the +calling thread. + +When a thread is created, its TSD area initially associates @code{NULL} +with all keys. + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_key_create (pthread_key_t *@var{key}, void (*destr_function) (void *)) +@code{pthread_key_create} allocates a new TSD key. The key is stored in +the location pointed to by @var{key}. There is a limit of +@code{PTHREAD_KEYS_MAX} on the number of keys allocated at a given +time. The value initially associated with the returned key is +@code{NULL} in all currently executing threads. + +The @var{destr_function} argument, if not @code{NULL}, specifies a +destructor function associated with the key. When a thread terminates +via @code{pthread_exit} or by cancellation, @var{destr_function} is +called on the value associated with the key in that thread. The +@var{destr_function} is not called if a key is deleted with +@code{pthread_key_delete} or a value is changed with +@code{pthread_setspecific}.  The order in which destructor functions are +called at thread termination time is unspecified. + +Before the destructor function is called, the @code{NULL} value is +associated with the key in the current thread.  A destructor function +might, however, re-associate non-@code{NULL} values to that key or some +other key.  To deal with this, if after all the destructors have been +called for all non-@code{NULL} values, there are still some +non-@code{NULL} values with associated destructors, then the process is +repeated.  The LinuxThreads implementation stops the process after +@code{PTHREAD_DESTRUCTOR_ITERATIONS} iterations, even if some +non-@code{NULL} values with associated descriptors remain.  Other +implementations may loop indefinitely. + +@code{pthread_key_create} returns 0 unless @code{PTHREAD_KEYS_MAX} keys +have already been allocated, in which case it fails and returns +@code{EAGAIN}. +@end deftypefun + + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_key_delete (pthread_key_t @var{key}) +@code{pthread_key_delete} deallocates a TSD key. It does not check +whether non-@code{NULL} values are associated with that key in the +currently executing threads, nor call the destructor function associated +with the key. + +If there is no such key @var{key}, it returns @code{EINVAL}.  Otherwise +it returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_setspecific (pthread_key_t @var{key}, const void *@var{pointer}) +@code{pthread_setspecific} changes the value associated with @var{key} +in the calling thread, storing the given @var{pointer} instead. + +If there is no such key @var{key}, it returns @code{EINVAL}.  Otherwise +it returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun {void *} pthread_getspecific (pthread_key_t @var{key}) +@code{pthread_getspecific} returns the value currently associated with +@var{key} in the calling thread. + +If there is no such key @var{key}, it returns @code{NULL}. +@end deftypefun + +The following code fragment allocates a thread-specific array of 100 +characters, with automatic reclaimation at thread exit: + +@smallexample +/* Key for the thread-specific buffer */ +static pthread_key_t buffer_key; + +/* Once-only initialisation of the key */ +static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT; + +/* Allocate the thread-specific buffer */ +void buffer_alloc(void) +@{ +  pthread_once(&buffer_key_once, buffer_key_alloc); +  pthread_setspecific(buffer_key, malloc(100)); +@} + +/* Return the thread-specific buffer */ +char * get_buffer(void) +@{ +  return (char *) pthread_getspecific(buffer_key); +@} + +/* Allocate the key */ +static void buffer_key_alloc() +@{ +  pthread_key_create(&buffer_key, buffer_destroy); +@} + +/* Free the thread-specific buffer */ +static void buffer_destroy(void * buf) +@{ +  free(buf); +@} +@end smallexample + +@node Threads and Signal Handling +@section Threads and Signal Handling + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_sigmask (int @var{how}, const sigset_t *@var{newmask}, sigset_t *@var{oldmask}) +@code{pthread_sigmask} changes the signal mask for the calling thread as +described by the @var{how} and @var{newmask} arguments. If @var{oldmask} +is not @code{NULL}, the previous signal mask is stored in the location +pointed to by @var{oldmask}. + +The meaning of the @var{how} and @var{newmask} arguments is the same as +for @code{sigprocmask}. If @var{how} is @code{SIG_SETMASK}, the signal +mask is set to @var{newmask}. If @var{how} is @code{SIG_BLOCK}, the +signals specified to @var{newmask} are added to the current signal mask. +If @var{how} is @code{SIG_UNBLOCK}, the signals specified to +@var{newmask} are removed from the current signal mask. + +Recall that signal masks are set on a per-thread basis, but signal +actions and signal handlers, as set with @code{sigaction}, are shared +between all threads. + +The @code{pthread_sigmask} function returns 0 on success, and one of the +following error codes on error: +@table @code +@item EINVAL +@var{how} is not one of @code{SIG_SETMASK}, @code{SIG_BLOCK}, or @code{SIG_UNBLOCK} + +@item EFAULT +@var{newmask} or @var{oldmask} point to invalid addresses +@end table +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_kill (pthread_t @var{thread}, int @var{signo}) +@code{pthread_kill} sends signal number @var{signo} to the thread +@var{thread}.  The signal is delivered and handled as described in +@ref{Signal Handling}. + +@code{pthread_kill} returns 0 on success, one of the following error codes +on error: +@table @code +@item EINVAL +@var{signo} is not a valid signal number + +@item ESRCH +The thread @var{thread} does not exist (e.g. it has already terminated) +@end table +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int sigwait (const sigset_t *@var{set}, int *@var{sig}) +@code{sigwait} suspends the calling thread until one of the signals in +@var{set} is delivered to the calling thread. It then stores the number +of the signal received in the location pointed to by @var{sig} and +returns. The signals in @var{set} must be blocked and not ignored on +entrance to @code{sigwait}. If the delivered signal has a signal handler +function attached, that function is @emph{not} called. + +@code{sigwait} is a cancellation point.  It always returns 0. +@end deftypefun + +For @code{sigwait} to work reliably, the signals being waited for must be +blocked in all threads, not only in the calling thread, since +otherwise the POSIX semantics for signal delivery do not guarantee +that it's the thread doing the @code{sigwait} that will receive the signal. +The best way to achieve this is block those signals before any threads +are created, and never unblock them in the program other than by +calling @code{sigwait}. + +Signal handling in LinuxThreads departs significantly from the POSIX +standard. According to the standard, ``asynchronous'' (external) signals +are addressed to the whole process (the collection of all threads), +which then delivers them to one particular thread. The thread that +actually receives the signal is any thread that does not currently block +the signal. + +In LinuxThreads, each thread is actually a kernel process with its own +PID, so external signals are always directed to one particular thread. +If, for instance, another thread is blocked in @code{sigwait} on that +signal, it will not be restarted. + +The LinuxThreads implementation of @code{sigwait} installs dummy signal +handlers for the signals in @var{set} for the duration of the +wait. Since signal handlers are shared between all threads, other +threads must not attach their own signal handlers to these signals, or +alternatively they should all block these signals (which is recommended +anyway). + +@node Miscellaneous Thread Functions +@section Miscellaneous Thread Functions + +@comment pthread.h +@comment POSIX +@deftypefun {pthread_t} pthread_self (@var{void}) +@code{pthread_self} returns the thread identifier for the calling thread. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_equal (pthread_t thread1, pthread_t thread2) +@code{pthread_equal} determines if two thread identifiers refer to the same +thread. + +A non-zero value is returned if @var{thread1} and @var{thread2} refer to +the same thread. Otherwise, 0 is returned. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_detach (pthread_t @var{th}) +@code{pthread_detach} puts the thread @var{th} in the detached +state. This guarantees that the memory resources consumed by @var{th} +will be freed immediately when @var{th} terminates. However, this +prevents other threads from synchronizing on the termination of @var{th} +using @code{pthread_join}. + +A thread can be created initially in the detached state, using the +@code{detachstate} attribute to @code{pthread_create}. In contrast, +@code{pthread_detach} applies to threads created in the joinable state, +and which need to be put in the detached state later. + +After @code{pthread_detach} completes, subsequent attempts to perform +@code{pthread_join} on @var{th} will fail. If another thread is already +joining the thread @var{th} at the time @code{pthread_detach} is called, +@code{pthread_detach} does nothing and leaves @var{th} in the joinable +state. + +On success, 0 is returned. On error, one of the following codes is +returned: +@table @code +@item ESRCH +No thread could be found corresponding to that specified by @var{th} +@item EINVAL +The thread @var{th} is already in the detached state +@end table +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_atfork (void (*@var{prepare})(void), void (*@var{parent})(void), void (*@var{child})(void)) + +@code{pthread_atfork} registers handler functions to be called just +before and just after a new process is created with @code{fork}. The +@var{prepare} handler will be called from the parent process, just +before the new process is created. The @var{parent} handler will be +called from the parent process, just before @code{fork} returns. The +@var{child} handler will be called from the child process, just before +@code{fork} returns. + +@code{pthread_atfork} returns 0 on success and a non-zero error code on +error. + +One or more of the three handlers @var{prepare}, @var{parent} and +@var{child} can be given as @code{NULL}, meaning that no handler needs +to be called at the corresponding point. + +@code{pthread_atfork} can be called several times to install several +sets of handlers. At @code{fork} time, the @var{prepare} handlers are +called in LIFO order (last added with @code{pthread_atfork}, first +called before @code{fork}), while the @var{parent} and @var{child} +handlers are called in FIFO order (first added, first called). + +If there is insufficient memory available to register the handlers, +@code{pthread_atfork} fails and returns @code{ENOMEM}.  Otherwise it +returns 0. +@end deftypefun + +To understand the purpose of @code{pthread_atfork}, recall that +@code{fork} duplicates the whole memory space, including mutexes in +their current locking state, but only the calling thread: other threads +are not running in the child process. Thus, if a mutex is locked by a +thread other than the thread calling @code{fork}, that mutex will remain +locked forever in the child process, possibly blocking the execution of +the child process. To avoid this, install handlers with +@code{pthread_atfork} as follows: the @var{prepare} handler locks the +global mutexes (in locking order), and the @var{parent} and @var{child} +handlers unlock them (in reverse order). Alternatively, @var{prepare} +and @var{parent} can be set to @code{NULL} and @var{child} to a function +that calls @code{pthread_mutex_init} on the global mutexes. + +@comment pthread.h +@comment GNU +@deftypefun void pthread_kill_other_threads_np (@var{void}) +@code{pthread_kill_other_threads_np} is a non-portable LinuxThreads extension. +It causes all threads in the program to terminate immediately, except +the calling thread which proceeds normally. It is intended to be +called just before a thread calls one of the @code{exec} functions, +e.g. @code{execve}. + +Termination of the other threads is not performed through +@code{pthread_cancel} and completely bypasses the cancellation +mechanism. Hence, the current settings for cancellation state and +cancellation type are ignored, and the cleanup handlers are not +executed in the terminated threads. + +According to POSIX 1003.1c, a successful @code{exec*} in one of the +threads should automatically terminate all other threads in the program. +This behavior is not yet implemented in LinuxThreads.  Calling +@code{pthread_kill_other_threads_np} before @code{exec*} achieves much +of the same behavior, except that if @code{exec*} ultimately fails, then +all other threads are already killed. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_once (pthread_once_t *once_@var{control}, void (*@var{init_routine}) (void)) + +The purpose of @code{pthread_once} is to ensure that a piece of +initialization code is executed at most once. The @var{once_control} +argument points to a static or extern variable statically initialized +to @code{PTHREAD_ONCE_INIT}. + +The first time @code{pthread_once} is called with a given +@var{once_control} argument, it calls @var{init_routine} with no +argument and changes the value of the @var{once_control} variable to +record that initialization has been performed. Subsequent calls to +@code{pthread_once} with the same @code{once_control} argument do +nothing. + +@code{pthread_once} always returns 0. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_setschedparam (pthread_t target_@var{thread}, int @var{policy}, const struct sched_param *@var{param}) + +@code{pthread_setschedparam} sets the scheduling parameters for the +thread @var{target_thread} as indicated by @var{policy} and +@var{param}. @var{policy} can be either @code{SCHED_OTHER} (regular, +non-realtime scheduling), @code{SCHED_RR} (realtime, round-robin) or +@code{SCHED_FIFO} (realtime, first-in first-out). @var{param} specifies +the scheduling priority for the two realtime policies.  See +@code{sched_setpolicy} for more information on scheduling policies. + +The realtime scheduling policies @code{SCHED_RR} and @code{SCHED_FIFO} +are available only to processes with superuser privileges. + +On success, @code{pthread_setschedparam} returns 0.  On error it returns +one of the following codes: +@table @code +@item EINVAL +@var{policy} is not one of @code{SCHED_OTHER}, @code{SCHED_RR}, +@code{SCHED_FIFO}, or the priority value specified by @var{param} is not +valid for the specified policy + +@item EPERM +Realtime scheduling was requested but the calling process does not have +sufficient privileges. + +@item ESRCH +The @var{target_thread} is invalid or has already terminated + +@item EFAULT +@var{param} points outside the process memory space +@end table +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_getschedparam (pthread_t target_@var{thread}, int *@var{policy}, struct sched_param *@var{param}) + +@code{pthread_getschedparam} retrieves the scheduling policy and +scheduling parameters for the thread @var{target_thread} and stores them +in the locations pointed to by @var{policy} and @var{param}, +respectively. + +@code{pthread_getschedparam} returns 0 on success, or one of the +following error codes on failure: +@table @code +@item ESRCH +The @var{target_thread} is invalid or has already terminated. + +@item EFAULT +@var{policy} or @var{param} point outside the process memory space. + +@end table +@end deftypefun diff --git a/libpthread/linuxthreads/lockfile.c b/libpthread/linuxthreads/lockfile.c new file mode 100644 index 000000000..18c3fed8c --- /dev/null +++ b/libpthread/linuxthreads/lockfile.c @@ -0,0 +1,88 @@ +/* lockfile - Handle locking and unlocking of stream. +   Copyright (C) 1996, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <bits/libc-lock.h> +#include <stdio.h> +#include <pthread.h> + +#ifdef USE_IN_LIBIO +#include "../libio/libioP.h" +#endif + +void +__flockfile (FILE *stream) +{ +#ifdef USE_IN_LIBIO +  __pthread_mutex_lock (stream->_lock); +#else +#endif +} +#ifdef USE_IN_LIBIO +#undef _IO_flockfile +strong_alias (__flockfile, _IO_flockfile) +#endif +weak_alias (__flockfile, flockfile); + + +void +__funlockfile (FILE *stream) +{ +#ifdef USE_IN_LIBIO +  __pthread_mutex_unlock (stream->_lock); +#else +#endif +} +#ifdef USE_IN_LIBIO +#undef _IO_funlockfile +strong_alias (__funlockfile, _IO_funlockfile) +#endif +weak_alias (__funlockfile, funlockfile); + + +int +__ftrylockfile (FILE *stream) +{ +#ifdef USE_IN_LIBIO +  return __pthread_mutex_trylock (stream->_lock); +#else +  return 0; +#endif +} +#ifdef USE_IN_LIBIO +strong_alias (__ftrylockfile, _IO_ftrylockfile) +#endif +weak_alias (__ftrylockfile, ftrylockfile); + + +void +__fresetlockfiles (void) +{ +#ifdef USE_IN_LIBIO +  _IO_FILE *fp; +  pthread_mutexattr_t attr; + +  __pthread_mutexattr_init (&attr); +  __pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE_NP); + +  for (fp = _IO_list_all; fp != NULL; fp = fp->_chain) +    __pthread_mutex_init (fp->_lock, &attr); + +  __pthread_mutexattr_destroy (&attr); +#endif +} diff --git a/libpthread/linuxthreads/manager.c b/libpthread/linuxthreads/manager.c new file mode 100644 index 000000000..5d355e107 --- /dev/null +++ b/libpthread/linuxthreads/manager.c @@ -0,0 +1,786 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* The "thread manager" thread: manages creation and termination of threads */ + +/* mods for uClibc: getpwd and getpagesize are the syscalls */ +#define __getpid getpid +#define __getpagesize getpagesize + +#include <errno.h> +#include <sched.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/poll.h>		/* for poll */ +#include <sys/mman.h>           /* for mmap */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/wait.h>           /* for waitpid macros */ + +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" +#include "semaphore.h" +#include "debug.h" /* PDEBUG, added by StS */ + +/* Array of active threads. Entry 0 is reserved for the initial thread. */ +struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] = +{ { LOCK_INITIALIZER, &__pthread_initial_thread, 0}, +  { LOCK_INITIALIZER, &__pthread_manager_thread, 0}, /* All NULLs */ }; + +/* For debugging purposes put the maximum number of threads in a variable.  */ +const int __linuxthreads_pthread_threads_max = PTHREAD_THREADS_MAX; + +/* Indicate whether at least one thread has a user-defined stack (if 1), +   or if all threads have stacks supplied by LinuxThreads (if 0). */ +int __pthread_nonstandard_stacks; + +/* Number of active entries in __pthread_handles (used by gdb) */ +volatile int __pthread_handles_num = 2; + +/* Whether to use debugger additional actions for thread creation +   (set to 1 by gdb) */ +volatile int __pthread_threads_debug; + +/* Globally enabled events.  */ +volatile td_thr_events_t __pthread_threads_events; + +/* Pointer to thread descriptor with last event.  */ +volatile pthread_descr __pthread_last_event; + +/* Mapping from stack segment to thread descriptor. */ +/* Stack segment numbers are also indices into the __pthread_handles array. */ +/* Stack segment number 0 is reserved for the initial thread. */ + +static inline pthread_descr thread_segment(int seg) +{ +  return (pthread_descr)(THREAD_STACK_START_ADDRESS - (seg - 1) * STACK_SIZE) +         - 1; +} + +/* Flag set in signal handler to record child termination */ + +static volatile int terminated_children = 0; + +/* Flag set when the initial thread is blocked on pthread_exit waiting +   for all other threads to terminate */ + +static int main_thread_exiting = 0; + +/* Counter used to generate unique thread identifier. +   Thread identifier is pthread_threads_counter + segment. */ + +static pthread_t pthread_threads_counter = 0; + +/* Forward declarations */ + +static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, +                                 void * (*start_routine)(void *), void *arg, +                                 sigset_t *mask, int father_pid, +				 int report_events, +				 td_thr_events_t *event_maskp); +static void pthread_handle_free(pthread_t th_id); +static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode); +static void pthread_reap_children(void); +static void pthread_kill_all_threads(int sig, int main_thread_also); + +/* The server thread managing requests for thread creation and termination */ + +int __pthread_manager(void *arg) +{ +  int reqfd = (int) (long int) arg; +  struct pollfd ufd; +  sigset_t mask; +  int n; +  struct pthread_request request; + +  /* If we have special thread_self processing, initialize it.  */ +#ifdef INIT_THREAD_SELF +  INIT_THREAD_SELF(&__pthread_manager_thread, 1); +#endif +  /* Set the error variable.  */ +  __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; +  __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; +  /* Block all signals except __pthread_sig_cancel and SIGTRAP */ +  sigfillset(&mask); +  sigdelset(&mask, __pthread_sig_cancel); /* for thread termination */ +  sigdelset(&mask, SIGTRAP);            /* for debugging purposes */ +  sigprocmask(SIG_SETMASK, &mask, NULL); +  /* Raise our priority to match that of main thread */ +  __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); +  /* Synchronize debugging of the thread manager */ +  n = __libc_read(reqfd, (char *)&request, sizeof(request)); +  ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG); +  ufd.fd = reqfd; +  ufd.events = POLLIN; +  /* Enter server loop */ +  while(1) { +PDEBUG("before poll\n"); +    n = poll(&ufd, 1, 2000); +PDEBUG("after poll\n"); + +    /* Check for termination of the main thread */ +    if (getppid() == 1) { +      pthread_kill_all_threads(SIGKILL, 0); +      _exit(0); +    } +    /* Check for dead children */ +    if (terminated_children) { +      terminated_children = 0; +      pthread_reap_children(); +    } +    /* Read and execute request */ +    if (n == 1 && (ufd.revents & POLLIN)) { +PDEBUG("before __libc_read\n"); +      n = __libc_read(reqfd, (char *)&request, sizeof(request)); +PDEBUG("after __libc_read, n=%d\n", n); +      ASSERT(n == sizeof(request)); +      switch(request.req_kind) { +      case REQ_CREATE: +PDEBUG("got REQ_CREATE\n"); +        request.req_thread->p_retcode = +          pthread_handle_create((pthread_t *) &request.req_thread->p_retval, +                                request.req_args.create.attr, +                                request.req_args.create.fn, +                                request.req_args.create.arg, +                                &request.req_args.create.mask, +                                request.req_thread->p_pid, +				request.req_thread->p_report_events, +				&request.req_thread->p_eventbuf.eventmask); +PDEBUG("restarting %d\n", request.req_thread); +        restart(request.req_thread); +        break; +      case REQ_FREE: +PDEBUG("got REQ_FREE\n"); +	pthread_handle_free(request.req_args.free.thread_id); +        break; +      case REQ_PROCESS_EXIT: +PDEBUG("got REQ_PROCESS_EXIT from %d, exit code = %d\n",  +       request.req_thread, request.req_args.exit.code); +        pthread_handle_exit(request.req_thread, +                            request.req_args.exit.code); +        break; +      case REQ_MAIN_THREAD_EXIT: +PDEBUG("got REQ_MAIN_THREAD_EXIT\n"); +        main_thread_exiting = 1; +        if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { +          restart(__pthread_main_thread); +          return 0; +        } +        break; +      case REQ_POST: +PDEBUG("got REQ_POST\n"); +        __new_sem_post(request.req_args.post); +        break; +      case REQ_DEBUG: +PDEBUG("got REQ_DEBUG\n"); +	/* Make gdb aware of new thread and gdb will restart the +	   new thread when it is ready to handle the new thread. */ +	if (__pthread_threads_debug && __pthread_sig_debug > 0) +PDEBUG("about to call raise(__pthread_sig_debug)\n"); +	  raise(__pthread_sig_debug); +        break; +      } +    } +  } +} + +int __pthread_manager_event(void *arg) +{ +  /* If we have special thread_self processing, initialize it.  */ +#ifdef INIT_THREAD_SELF +  INIT_THREAD_SELF(&__pthread_manager_thread, 1); +#endif + +  /* Get the lock the manager will free once all is correctly set up.  */ +  __pthread_lock (THREAD_GETMEM((&__pthread_manager_thread), p_lock), NULL); +  /* Free it immediately.  */ +  __pthread_unlock (THREAD_GETMEM((&__pthread_manager_thread), p_lock)); + +  return __pthread_manager(arg); +} + +/* Process creation */ + +static int pthread_start_thread(void *arg) +{ +  pthread_descr self = (pthread_descr) arg; +  struct pthread_request request; +  void * outcome; +  /* Initialize special thread_self processing, if any.  */ +#ifdef INIT_THREAD_SELF +  INIT_THREAD_SELF(self, self->p_nr); +#endif +PDEBUG("\n"); +  /* Make sure our pid field is initialized, just in case we get there +     before our father has initialized it. */ +  THREAD_SETMEM(self, p_pid, __getpid()); +  /* Initial signal mask is that of the creating thread. (Otherwise, +     we'd just inherit the mask of the thread manager.) */ +  sigprocmask(SIG_SETMASK, &self->p_start_args.mask, NULL); +  /* Set the scheduling policy and priority for the new thread, if needed */ +  if (THREAD_GETMEM(self, p_start_args.schedpolicy) >= 0) +    /* Explicit scheduling attributes were provided: apply them */ +    sched_setscheduler(THREAD_GETMEM(self, p_pid), +			 THREAD_GETMEM(self, p_start_args.schedpolicy), +                         &self->p_start_args.schedparam); +  else if (__pthread_manager_thread.p_priority > 0) +    /* Default scheduling required, but thread manager runs in realtime +       scheduling: switch new thread to SCHED_OTHER policy */ +    { +      struct sched_param default_params; +      default_params.sched_priority = 0; +      sched_setscheduler(THREAD_GETMEM(self, p_pid), +                           SCHED_OTHER, &default_params); +    } +  /* Make gdb aware of new thread */ +  if (__pthread_threads_debug && __pthread_sig_debug > 0) { +    request.req_thread = self; +    request.req_kind = REQ_DEBUG; +    __libc_write(__pthread_manager_request, +                 (char *) &request, sizeof(request)); +    suspend(self); +  } +  /* Run the thread code */ +  outcome = self->p_start_args.start_routine(THREAD_GETMEM(self, +							   p_start_args.arg)); +  /* Exit with the given return value */ +  pthread_exit(outcome); +  return 0; +} + +static int pthread_start_thread_event(void *arg) +{ +  pthread_descr self = (pthread_descr) arg; + +#ifdef INIT_THREAD_SELF +  INIT_THREAD_SELF(self, self->p_nr); +#endif +  /* Make sure our pid field is initialized, just in case we get there +     before our father has initialized it. */ +  THREAD_SETMEM(self, p_pid, __getpid()); +  /* Get the lock the manager will free once all is correctly set up.  */ +  __pthread_lock (THREAD_GETMEM(self, p_lock), NULL); +  /* Free it immediately.  */ +  __pthread_unlock (THREAD_GETMEM(self, p_lock)); + +  /* Continue with the real function.  */ +  return pthread_start_thread (arg); +} + +static int pthread_allocate_stack(const pthread_attr_t *attr, +                                  pthread_descr default_new_thread, +                                  int pagesize, +                                  pthread_descr * out_new_thread, +                                  char ** out_new_thread_bottom, +                                  char ** out_guardaddr, +                                  size_t * out_guardsize) +{ +  pthread_descr new_thread; +  char * new_thread_bottom; +  char * guardaddr; +  size_t stacksize, guardsize; + +  if (attr != NULL && attr->__stackaddr_set) +    { +      /* The user provided a stack. */ +      new_thread = +        (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1; +      new_thread_bottom = (char *) attr->__stackaddr - attr->__stacksize; +      guardaddr = NULL; +      guardsize = 0; +      __pthread_nonstandard_stacks = 1; +    } +  else +    { +#ifdef __UCLIBC_HAS_MMU__ +      stacksize = STACK_SIZE - pagesize; +      if (attr != NULL) +        stacksize = MIN (stacksize, roundup(attr->__stacksize, pagesize)); +      /* Allocate space for stack and thread descriptor at default address */ +      new_thread = default_new_thread; +      new_thread_bottom = (char *) (new_thread + 1) - stacksize; +      if (mmap((caddr_t)((char *)(new_thread + 1) - INITIAL_STACK_SIZE), +               INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, +               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, +               -1, 0) == MAP_FAILED) +        /* Bad luck, this segment is already mapped. */ +        return -1; +      /* We manage to get a stack.  Now see whether we need a guard +         and allocate it if necessary.  Notice that the default +         attributes (stack_size = STACK_SIZE - pagesize) do not need +	 a guard page, since the RLIMIT_STACK soft limit prevents stacks +	 from running into one another. */ +      if (stacksize == STACK_SIZE - pagesize) +        { +          /* We don't need a guard page. */ +          guardaddr = NULL; +          guardsize = 0; +        } +      else +        { +          /* Put a bad page at the bottom of the stack */ +          guardsize = attr->__guardsize; +          guardaddr = (void *)new_thread_bottom - guardsize; +          if (mmap ((caddr_t) guardaddr, guardsize, 0, MAP_FIXED, -1, 0) +              == MAP_FAILED) +            { +              /* We don't make this an error.  */ +              guardaddr = NULL; +              guardsize = 0; +            } +        } +#else +      /* We cannot mmap to this huge chunk of stack space when we don't have +       * an MMU. Pretend we are using a user provided stack even if there was +       * none provided by the user. Thus, we get around the mmap and reservation +       * of a huge stack segment. -StS */ + +      char *new_stack; + +      if ((new_stack = malloc(INITIAL_STACK_SIZE)) == NULL) { +	/* bad luck, we cannot malloc any more */ +	return -1; +      } + +      PDEBUG("malloced chunk: base=%p, size=0x%04x\n", new_stack, INITIAL_STACK_SIZE); + +      /* Set up the pointers. new_thread marks the TOP of the stack frame and +       * the address of the pthread_descr struct at the same time. Therefore we +       * must account for its size and fit it in the malloc()'ed block. The +       * value of `new_thread' is then passed to clone() as the stack argument. +       * +       *               ^ +------------------------+ +       *               | |  pthread_descr struct  | +       *               | +------------------------+  <- new_thread  +       * malloc block  | |                        | +       *               | |  thread stack          | +       *               | |                        | +       *               v +------------------------+  <- new_thread_bottom +       * +       * Note: The calculated value of new_thread must be word aligned otherwise +       * the kernel chokes on a non-aligned stack frame. Choose the lower +       * available word boundary. +       */ +      new_thread_bottom = (pthread_descr) new_stack; +      new_thread = (long)((char *) new_stack + INITIAL_STACK_SIZE - sizeof(*new_thread) - 1)  +	  & -sizeof(void*); /* align new_thread */ +      guardaddr = NULL; +      guardsize = 0; + +      PDEBUG("thread stack: bos=%p, tos=%p\n", new_thread_bottom, new_thread); + +      /* check the initial thread stack boundaries so they don't overlap */ +      NOMMU_INITIAL_THREAD_BOUNDS(new_thread, new_thread_bottom); + +      PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos,  +	     __pthread_initial_thread_tos); + +      /* on non-MMU systems we always have non-standard stack frames */ +      __pthread_nonstandard_stacks = 1; +       +#endif /* __UCLIBC_HAS_MMU__ */ +    } + +  /* Clear the thread data structure.  */ +  memset (new_thread, '\0', sizeof (*new_thread)); +  *out_new_thread = new_thread; +  *out_new_thread_bottom = new_thread_bottom; +  *out_guardaddr = guardaddr; +  *out_guardsize = guardsize; +  return 0; +} + +static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, +				 void * (*start_routine)(void *), void *arg, +				 sigset_t * mask, int father_pid, +				 int report_events, +				 td_thr_events_t *event_maskp) +{ +  size_t sseg; +  int pid; +  pthread_descr new_thread; +  char * new_thread_bottom; +  pthread_t new_thread_id; +  char *guardaddr = NULL; +  size_t guardsize = 0; +  int pagesize = __getpagesize(); + +  /* First check whether we have to change the policy and if yes, whether +     we can  do this.  Normally this should be done by examining the +     return value of the sched_setscheduler call in pthread_start_thread +     but this is hard to implement.  FIXME  */ +  if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0) +    return EPERM; +  /* Find a free segment for the thread, and allocate a stack if needed */ +  for (sseg = 2; ; sseg++) +    { +      if (sseg >= PTHREAD_THREADS_MAX) +	return EAGAIN; +      if (__pthread_handles[sseg].h_descr != NULL) +	continue; +      if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize, +                                 &new_thread, &new_thread_bottom, +                                 &guardaddr, &guardsize) == 0) +        break; +    } +  __pthread_handles_num++; +  /* Allocate new thread identifier */ +  pthread_threads_counter += PTHREAD_THREADS_MAX; +  new_thread_id = sseg + pthread_threads_counter; +  /* Initialize the thread descriptor.  Elements which have to be +     initialized to zero already have this value.  */ +  new_thread->p_tid = new_thread_id; +  new_thread->p_lock = &(__pthread_handles[sseg].h_lock); +  new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE; +  new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED; +  new_thread->p_errnop = &new_thread->p_errno; +  new_thread->p_h_errnop = &new_thread->p_h_errno; +  new_thread->p_guardaddr = guardaddr; +  new_thread->p_guardsize = guardsize; +  new_thread->p_self = new_thread; +  new_thread->p_nr = sseg; +  /* Initialize the thread handle */ +  __pthread_init_lock(&__pthread_handles[sseg].h_lock); +  __pthread_handles[sseg].h_descr = new_thread; +  __pthread_handles[sseg].h_bottom = new_thread_bottom; +  /* Determine scheduling parameters for the thread */ +  new_thread->p_start_args.schedpolicy = -1; +  if (attr != NULL) { +    new_thread->p_detached = attr->__detachstate; +    new_thread->p_userstack = attr->__stackaddr_set; + +    switch(attr->__inheritsched) { +    case PTHREAD_EXPLICIT_SCHED: +      new_thread->p_start_args.schedpolicy = attr->__schedpolicy; +      memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam, +	      sizeof (struct sched_param)); +      break; +    case PTHREAD_INHERIT_SCHED: +      new_thread->p_start_args.schedpolicy = sched_getscheduler(father_pid); +      sched_getparam(father_pid, &new_thread->p_start_args.schedparam); +      break; +    } +    new_thread->p_priority = +      new_thread->p_start_args.schedparam.sched_priority; +  } +  /* Finish setting up arguments to pthread_start_thread */ +  new_thread->p_start_args.start_routine = start_routine; +  new_thread->p_start_args.arg = arg; +  new_thread->p_start_args.mask = *mask; +  /* Raise priority of thread manager if needed */ +  __pthread_manager_adjust_prio(new_thread->p_priority); +  /* Do the cloning.  We have to use two different functions depending +     on whether we are debugging or not.  */ +  pid = 0;     /* Note that the thread never can have PID zero.  */ +  if (report_events) +    { +      /* See whether the TD_CREATE event bit is set in any of the +         masks.  */ +      int idx = __td_eventword (TD_CREATE); +      uint32_t mask = __td_eventmask (TD_CREATE); + +      if ((mask & (__pthread_threads_events.event_bits[idx] +		   | event_maskp->event_bits[idx])) != 0) +	{ +	  /* Lock the mutex the child will use now so that it will stop.  */ +	  __pthread_lock(new_thread->p_lock, NULL); + +	  /* We have to report this event.  */ +	  pid = clone(pthread_start_thread_event, (void **) new_thread, +		        CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | +		        __pthread_sig_cancel, new_thread); +	  if (pid != -1) +	    { +	      /* Now fill in the information about the new thread in +	         the newly created thread's data structure.  We cannot let +	         the new thread do this since we don't know whether it was +	         already scheduled when we send the event.  */ +	      new_thread->p_eventbuf.eventdata = new_thread; +	      new_thread->p_eventbuf.eventnum = TD_CREATE; +	      __pthread_last_event = new_thread; + +	      /* We have to set the PID here since the callback function +		 in the debug library will need it and we cannot guarantee +		 the child got scheduled before the debugger.  */ +	      new_thread->p_pid = pid; + +	      /* Now call the function which signals the event.  */ +	      __linuxthreads_create_event (); + +	      /* Now restart the thread.  */ +	      __pthread_unlock(new_thread->p_lock); +	    } +	} +    } +  if (pid == 0) +PDEBUG("cloning new_thread = %p\n", new_thread); +    pid = clone(pthread_start_thread, (void **) new_thread, +		  CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | +		  __pthread_sig_cancel, new_thread); +  /* Check if cloning succeeded */ +  if (pid == -1) { +    /* Free the stack if we allocated it */ +    if (attr == NULL || !attr->__stackaddr_set) +      { +#ifdef __UCLIBC_HAS_MMU__ +	if (new_thread->p_guardsize != 0) +	  munmap(new_thread->p_guardaddr, new_thread->p_guardsize); +	munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE), +	       INITIAL_STACK_SIZE); +#else +	free(new_thread_bottom); +#endif /* __UCLIBC_HAS_MMU__ */ +      } +    __pthread_handles[sseg].h_descr = NULL; +    __pthread_handles[sseg].h_bottom = NULL; +    __pthread_handles_num--; +    return errno; +  } +PDEBUG("new thread pid = %d\n", pid); +  /* Insert new thread in doubly linked list of active threads */ +  new_thread->p_prevlive = __pthread_main_thread; +  new_thread->p_nextlive = __pthread_main_thread->p_nextlive; +  __pthread_main_thread->p_nextlive->p_prevlive = new_thread; +  __pthread_main_thread->p_nextlive = new_thread; +  /* Set pid field of the new thread, in case we get there before the +     child starts. */ +  new_thread->p_pid = pid; +  /* We're all set */ +  *thread = new_thread_id; +  return 0; +} + + +/* Try to free the resources of a thread when requested by pthread_join +   or pthread_detach on a terminated thread. */ + +static void pthread_free(pthread_descr th) +{ +  pthread_handle handle; +  pthread_readlock_info *iter, *next; +  char *h_bottom_save; + +  ASSERT(th->p_exited); +  /* Make the handle invalid */ +  handle =  thread_handle(th->p_tid); +  __pthread_lock(&handle->h_lock, NULL); +  h_bottom_save = handle->h_bottom; +  handle->h_descr = NULL; +  handle->h_bottom = (char *)(-1L); +  __pthread_unlock(&handle->h_lock); +#ifdef FREE_THREAD_SELF +  FREE_THREAD_SELF(th, th->p_nr); +#endif +  /* One fewer threads in __pthread_handles */ +  __pthread_handles_num--; + +  /* Destroy read lock list, and list of free read lock structures. +     If the former is not empty, it means the thread exited while +     holding read locks! */ + +  for (iter = th->p_readlock_list; iter != NULL; iter = next) +    { +      next = iter->pr_next; +      free(iter); +    } + +  for (iter = th->p_readlock_free; iter != NULL; iter = next) +    { +      next = iter->pr_next; +      free(iter); +    } + +  /* If initial thread, nothing to free */ +  if (th == &__pthread_initial_thread) return; +#ifdef __UCLIBC_HAS_MMU__ +  if (!th->p_userstack) +    { +      /* Free the stack and thread descriptor area */ +      if (th->p_guardsize != 0) +	munmap(th->p_guardaddr, th->p_guardsize); +      munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE); +    } +#else +  /* For non-MMU systems we always malloc the stack, so free it here. -StS */ +  if (!th->p_userstack) { +      free(h_bottom_save); +  } +#endif /* __UCLIBC_HAS_MMU__ */ +} + +/* Handle threads that have exited */ + +static void pthread_exited(pid_t pid) +{ +  pthread_descr th; +  int detached; +  /* Find thread with that pid */ +  for (th = __pthread_main_thread->p_nextlive; +       th != __pthread_main_thread; +       th = th->p_nextlive) { +    if (th->p_pid == pid) { +      /* Remove thread from list of active threads */ +      th->p_nextlive->p_prevlive = th->p_prevlive; +      th->p_prevlive->p_nextlive = th->p_nextlive; +      /* Mark thread as exited, and if detached, free its resources */ +      __pthread_lock(th->p_lock, NULL); +      th->p_exited = 1; +      /* If we have to signal this event do it now.  */ +      if (th->p_report_events) +	{ +	  /* See whether TD_DEATH is in any of the mask.  */ +	  int idx = __td_eventword (TD_REAP); +	  uint32_t mask = __td_eventmask (TD_REAP); + +	  if ((mask & (__pthread_threads_events.event_bits[idx] +		       | th->p_eventbuf.eventmask.event_bits[idx])) != 0) +	    { +	      /* Yep, we have to signal the death.  */ +	      th->p_eventbuf.eventnum = TD_DEATH; +	      th->p_eventbuf.eventdata = th; +	      __pthread_last_event = th; + +	      /* Now call the function to signal the event.  */ +	      __linuxthreads_reap_event(); +	    } +	} +      detached = th->p_detached; +      __pthread_unlock(th->p_lock); +      if (detached) +	pthread_free(th); +      break; +    } +  } +  /* If all threads have exited and the main thread is pending on a +     pthread_exit, wake up the main thread and terminate ourselves. */ +  if (main_thread_exiting && +      __pthread_main_thread->p_nextlive == __pthread_main_thread) { +    restart(__pthread_main_thread); +    _exit(0); +  } +} + +static void pthread_reap_children(void) +{ +  pid_t pid; +  int status; +PDEBUG("\n"); + +  while ((pid = __libc_waitpid(-1, &status, WNOHANG | __WCLONE)) > 0) { +    pthread_exited(pid); +    if (WIFSIGNALED(status)) { +      /* If a thread died due to a signal, send the same signal to +         all other threads, including the main thread. */ +      pthread_kill_all_threads(WTERMSIG(status), 1); +      _exit(0); +    } +  } +} + +/* Try to free the resources of a thread when requested by pthread_join +   or pthread_detach on a terminated thread. */ + +static void pthread_handle_free(pthread_t th_id) +{ +  pthread_handle handle = thread_handle(th_id); +  pthread_descr th; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, th_id)) { +    /* pthread_reap_children has deallocated the thread already, +       nothing needs to be done */ +    __pthread_unlock(&handle->h_lock); +    return; +  } +  th = handle->h_descr; +  if (th->p_exited) { +    __pthread_unlock(&handle->h_lock); +    pthread_free(th); +  } else { +    /* The Unix process of the thread is still running. +       Mark the thread as detached so that the thread manager will +       deallocate its resources when the Unix process exits. */ +    th->p_detached = 1; +    __pthread_unlock(&handle->h_lock); +  } +} + +/* Send a signal to all running threads */ + +static void pthread_kill_all_threads(int sig, int main_thread_also) +{ +  pthread_descr th; +  for (th = __pthread_main_thread->p_nextlive; +       th != __pthread_main_thread; +       th = th->p_nextlive) { +    kill(th->p_pid, sig); +  } +  if (main_thread_also) { +    kill(__pthread_main_thread->p_pid, sig); +  } +} + +/* Process-wide exit() */ + +static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode) +{ +  pthread_descr th; +  __pthread_exit_requested = 1; +  __pthread_exit_code = exitcode; +  /* Send the CANCEL signal to all running threads, including the main +     thread, but excluding the thread from which the exit request originated +     (that thread must complete the exit, e.g. calling atexit functions +     and flushing stdio buffers). */ +  for (th = issuing_thread->p_nextlive; +       th != issuing_thread; +       th = th->p_nextlive) { +    kill(th->p_pid, __pthread_sig_cancel); +  } +  /* Now, wait for all these threads, so that they don't become zombies +     and their times are properly added to the thread manager's times. */ +  for (th = issuing_thread->p_nextlive; +       th != issuing_thread; +       th = th->p_nextlive) { +    waitpid(th->p_pid, NULL, __WCLONE); +  } +  restart(issuing_thread); +  _exit(0); +} + +/* Handler for __pthread_sig_cancel in thread manager thread */ + +void __pthread_manager_sighandler(int sig) +{ +  terminated_children = 1; +} + +/* Adjust priority of thread manager so that it always run at a priority +   higher than all threads */ + +void __pthread_manager_adjust_prio(int thread_prio) +{ +  struct sched_param param; + +  if (thread_prio <= __pthread_manager_thread.p_priority) return; +  param.sched_priority = +    thread_prio < sched_get_priority_max(SCHED_FIFO) +    ? thread_prio + 1 : thread_prio; +  sched_setscheduler(__pthread_manager_thread.p_pid, SCHED_FIFO, ¶m); +  __pthread_manager_thread.p_priority = thread_prio; +} diff --git a/libpthread/linuxthreads/mutex.c b/libpthread/linuxthreads/mutex.c new file mode 100644 index 000000000..2217a504e --- /dev/null +++ b/libpthread/linuxthreads/mutex.c @@ -0,0 +1,201 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* changes for uClibc: remove strong_alias'es and define the real symbol */ + +/* Mutexes */ + +#include <errno.h> +#include <sched.h> +#include <stddef.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "queue.h" +#include "restart.h" + +int pthread_mutex_init(pthread_mutex_t * mutex, +                       const pthread_mutexattr_t * mutex_attr) +{ +  __pthread_init_lock(&mutex->__m_lock); +  mutex->__m_kind = +    mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->__mutexkind; +  mutex->__m_count = 0; +  mutex->__m_owner = NULL; +  return 0; +} +//strong_alias (__pthread_mutex_init, pthread_mutex_init) + +int pthread_mutex_destroy(pthread_mutex_t * mutex) +{ +  if (mutex->__m_lock.__status != 0) return EBUSY; +  return 0; +} +//strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) + +int pthread_mutex_trylock(pthread_mutex_t * mutex) +{ +  pthread_descr self; +  int retcode; + +  switch(mutex->__m_kind) { +  case PTHREAD_MUTEX_FAST_NP: +    retcode = __pthread_trylock(&mutex->__m_lock); +    return retcode; +  case PTHREAD_MUTEX_RECURSIVE_NP: +    self = thread_self(); +    if (mutex->__m_owner == self) { +      mutex->__m_count++; +      return 0; +    } +    retcode = __pthread_trylock(&mutex->__m_lock); +    if (retcode == 0) { +      mutex->__m_owner = self; +      mutex->__m_count = 0; +    } +    return retcode; +  case PTHREAD_MUTEX_ERRORCHECK_NP: +    retcode = __pthread_trylock(&mutex->__m_lock); +    if (retcode == 0) { +      mutex->__m_owner = thread_self(); +    } +    return retcode; +  default: +    return EINVAL; +  } +} +//strong_alias (__pthread_mutex_trylock, pthread_mutex_trylock) + +int pthread_mutex_lock(pthread_mutex_t * mutex) +{ +  pthread_descr self; + +  switch(mutex->__m_kind) { +  case PTHREAD_MUTEX_FAST_NP: +    __pthread_lock(&mutex->__m_lock, NULL); +    return 0; +  case PTHREAD_MUTEX_RECURSIVE_NP: +    self = thread_self(); +    if (mutex->__m_owner == self) { +      mutex->__m_count++; +      return 0; +    } +    __pthread_lock(&mutex->__m_lock, self); +    mutex->__m_owner = self; +    mutex->__m_count = 0; +    return 0; +  case PTHREAD_MUTEX_ERRORCHECK_NP: +    self = thread_self(); +    if (mutex->__m_owner == self) return EDEADLK; +    __pthread_lock(&mutex->__m_lock, self); +    mutex->__m_owner = self; +    return 0; +  default: +    return EINVAL; +  } +} +//strong_alias (__pthread_mutex_lock, pthread_mutex_lock) + +int pthread_mutex_unlock(pthread_mutex_t * mutex) +{ +  switch (mutex->__m_kind) { +  case PTHREAD_MUTEX_FAST_NP: +    __pthread_unlock(&mutex->__m_lock); +    return 0; +  case PTHREAD_MUTEX_RECURSIVE_NP: +    if (mutex->__m_count > 0) { +      mutex->__m_count--; +      return 0; +    } +    mutex->__m_owner = NULL; +    __pthread_unlock(&mutex->__m_lock); +    return 0; +  case PTHREAD_MUTEX_ERRORCHECK_NP: +    if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0) +      return EPERM; +    mutex->__m_owner = NULL; +    __pthread_unlock(&mutex->__m_lock); +    return 0; +  default: +    return EINVAL; +  } +} +//strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock) + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ +  attr->__mutexkind = PTHREAD_MUTEX_FAST_NP; +  return 0; +} +//strong_alias (__pthread_mutexattr_init, pthread_mutexattr_init) + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ +  return 0; +} +//strong_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy) + +int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind) +{ +  if (kind != PTHREAD_MUTEX_FAST_NP +      && kind != PTHREAD_MUTEX_RECURSIVE_NP +      && kind != PTHREAD_MUTEX_ERRORCHECK_NP) +    return EINVAL; +  attr->__mutexkind = kind; +  return 0; +} +weak_alias (__pthread_mutexattr_settype, pthread_mutexattr_settype) +weak_alias ( __pthread_mutexattr_settype, __pthread_mutexattr_setkind_np) +weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np) + +int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind) +{ +  *kind = attr->__mutexkind; +  return 0; +} +weak_alias (__pthread_mutexattr_gettype, pthread_mutexattr_gettype) +weak_alias (__pthread_mutexattr_gettype, __pthread_mutexattr_getkind_np) +weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np) + +/* Once-only execution */ + +static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER; + +enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 }; + +int pthread_once(pthread_once_t * once_control, void (*init_routine)(void)) +{ +  /* Test without locking first for speed */ +  if (*once_control == DONE) return 0; +  /* Lock and test again */ +  pthread_mutex_lock(&once_masterlock); +  /* If init_routine is being called from another routine, wait until +     it completes. */ +  while (*once_control == IN_PROGRESS) { +    pthread_cond_wait(&once_finished, &once_masterlock); +  } +  /* Here *once_control is stable and either NEVER or DONE. */ +  if (*once_control == NEVER) { +    *once_control = IN_PROGRESS; +    pthread_mutex_unlock(&once_masterlock); +    init_routine(); +    pthread_mutex_lock(&once_masterlock); +    *once_control = DONE; +    pthread_cond_broadcast(&once_finished); +  } +  pthread_mutex_unlock(&once_masterlock); +  return 0; +} +//strong_alias (__pthread_once, pthread_once) diff --git a/libpthread/linuxthreads/no-tsd.c b/libpthread/linuxthreads/no-tsd.c new file mode 100644 index 000000000..ef79cb832 --- /dev/null +++ b/libpthread/linuxthreads/no-tsd.c @@ -0,0 +1,33 @@ +/* libc-internal interface for thread-specific data. +   Copyright (C) 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <bits/libc-tsd.h> + +/* This file provides uinitialized (common) definitions for the +   hooks used internally by libc to access thread-specific data. + +   When -lpthread is used, it provides initialized definitions for these +   variables (in specific.c), which override these uninitialized definitions. + +   If -lpthread is not used, these uninitialized variables default to zero, +   which the __libc_tsd_* macros check for.   */ + +void *(*__libc_internal_tsd_get) __P ((enum __libc_tsd_key_t)); +int (*__libc_internal_tsd_set) __P ((enum __libc_tsd_key_t, +				     __const void *)); diff --git a/libpthread/linuxthreads/oldsemaphore.c b/libpthread/linuxthreads/oldsemaphore.c new file mode 100644 index 000000000..a634bad8e --- /dev/null +++ b/libpthread/linuxthreads/oldsemaphore.c @@ -0,0 +1,235 @@ +/* + * This file contains the old semaphore code that we need to + * preserve for glibc-2.0 backwards compatibility. Port to glibc 2.1 + * done by Cristian Gafton. + */ + +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Semaphores a la POSIX 1003.1b */ + +#include <errno.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" +#include "queue.h" + +typedef struct { +    long int sem_status; +    int sem_spinlock; +} old_sem_t; + +/* Maximum value the semaphore can have.  */ +#define SEM_VALUE_MAX   ((int) ((~0u) >> 1)) + +static inline int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval) +{ +    return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock); +} + +/* The state of a semaphore is represented by a long int encoding +   either the semaphore count if >= 0 and no thread is waiting on it, +   or the head of the list of threads waiting for the semaphore. +   To distinguish the two cases, we encode the semaphore count N +   as 2N+1, so that it has the lowest bit set. + +   A sequence of sem_wait operations on a semaphore initialized to N +   result in the following successive states: +     2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ... +*/ + +static void sem_restart_list(pthread_descr waiting); + +int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value) +{ +    if (value > SEM_VALUE_MAX) { +	errno = EINVAL; +	return -1; +    } +    if (pshared) { +	errno = ENOSYS; +	return -1; +    } +  sem->sem_spinlock = 0; +  sem->sem_status = ((long)value << 1) + 1; +  return 0; +} + +/* Function called by pthread_cancel to remove the thread from +   waiting inside __old_sem_wait. Here we simply unconditionally +   indicate that the thread is to be woken, by returning 1. */ + +static int old_sem_extricate_func(void *obj, pthread_descr th) +{ +    return 1; +} + +int __old_sem_wait(old_sem_t * sem) +{ +    long oldstatus, newstatus; +    volatile pthread_descr self = thread_self(); +    pthread_descr * th; +    pthread_extricate_if extr; + +    /* Set up extrication interface */ +    extr.pu_object = 0; +    extr.pu_extricate_func = old_sem_extricate_func; + +    while (1) { +	/* Register extrication interface */ +	__pthread_set_own_extricate_if(self, &extr);  +	do { +            oldstatus = sem->sem_status; +            if ((oldstatus & 1) && (oldstatus != 1)) +		newstatus = oldstatus - 2; +            else { +		newstatus = (long) self; +		self->p_nextwaiting = (pthread_descr) oldstatus; +	    } +	} +	while (! sem_compare_and_swap(sem, oldstatus, newstatus)); +	if (newstatus & 1) { +	    /* We got the semaphore. */ +	  __pthread_set_own_extricate_if(self, 0);  +	    return 0; +	} +	/* Wait for sem_post or cancellation */ +	suspend(self); +	__pthread_set_own_extricate_if(self, 0);  + +	/* This is a cancellation point */ +	if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { +	    /* Remove ourselves from the waiting list if we're still on it */ +	    /* First check if we're at the head of the list. */ +            do { +		oldstatus = sem->sem_status; +		if (oldstatus != (long) self) break; +		newstatus = (long) self->p_nextwaiting; +	    } +            while (! sem_compare_and_swap(sem, oldstatus, newstatus)); +            /* Now, check if we're somewhere in the list. +	       There's a race condition with sem_post here, but it does not matter: +	       the net result is that at the time pthread_exit is called, +	       self is no longer reachable from sem->sem_status. */ +            if (oldstatus != (long) self && (oldstatus & 1) == 0) { +		for (th = &(((pthread_descr) oldstatus)->p_nextwaiting); +		     *th != NULL && *th != (pthread_descr) 1; +		     th = &((*th)->p_nextwaiting)) { +		    if (*th == self) { +			*th = self->p_nextwaiting; +			break; +		    } +		} +	    } +            pthread_exit(PTHREAD_CANCELED); +	} +    } +} + +int __old_sem_trywait(old_sem_t * sem) +{ +  long oldstatus, newstatus; + +  do { +    oldstatus = sem->sem_status; +    if ((oldstatus & 1) == 0 || (oldstatus == 1)) { +      errno = EAGAIN; +      return -1; +    } +    newstatus = oldstatus - 2; +  } +  while (! sem_compare_and_swap(sem, oldstatus, newstatus)); +  return 0; +} + +int __old_sem_post(old_sem_t * sem) +{ +  long oldstatus, newstatus; + +  do { +    oldstatus = sem->sem_status; +    if ((oldstatus & 1) == 0) +      newstatus = 3; +    else { +      if (oldstatus >= SEM_VALUE_MAX) { +        /* Overflow */ +        errno = ERANGE; +        return -1; +      } +      newstatus = oldstatus + 2; +    } +  } +  while (! sem_compare_and_swap(sem, oldstatus, newstatus)); +  if ((oldstatus & 1) == 0) +    sem_restart_list((pthread_descr) oldstatus); +  return 0; +} + +int __old_sem_getvalue(old_sem_t * sem, int * sval) +{ +  long status = sem->sem_status; +  if (status & 1) +    *sval = (int)((unsigned long) status >> 1); +  else +    *sval = 0; +  return 0; +} + +int __old_sem_destroy(old_sem_t * sem) +{ +  if ((sem->sem_status & 1) == 0) { +    errno = EBUSY; +    return -1; +  } +  return 0; +} + +/* Auxiliary function for restarting all threads on a waiting list, +   in priority order. */ + +static void sem_restart_list(pthread_descr waiting) +{ +  pthread_descr th, towake, *p; + +  /* Sort list of waiting threads by decreasing priority (insertion sort) */ +  towake = NULL; +  while (waiting != (pthread_descr) 1) { +    th = waiting; +    waiting = waiting->p_nextwaiting; +    p = &towake; +    while (*p != NULL && th->p_priority < (*p)->p_priority) +      p = &((*p)->p_nextwaiting); +    th->p_nextwaiting = *p; +    *p = th; +  } +  /* Wake up threads in priority order */ +  while (towake != NULL) { +    th = towake; +    towake = towake->p_nextwaiting; +    th->p_nextwaiting = NULL; +    restart(th); +  } +} + +#if defined PIC && DO_VERSIONING +symbol_version (__old_sem_init, sem_init, GLIBC_2.0); +symbol_version (__old_sem_wait, sem_wait, GLIBC_2.0); +symbol_version (__old_sem_trywait, sem_trywait, GLIBC_2.0); +symbol_version (__old_sem_post, sem_post, GLIBC_2.0); +symbol_version (__old_sem_getvalue, sem_getvalue, GLIBC_2.0); +symbol_version (__old_sem_destroy, sem_destroy, GLIBC_2.0); +#endif + diff --git a/libpthread/linuxthreads/pt-machine.c b/libpthread/linuxthreads/pt-machine.c new file mode 100644 index 000000000..438008d5d --- /dev/null +++ b/libpthread/linuxthreads/pt-machine.c @@ -0,0 +1,22 @@ +/* "Instantiation of machine-dependent pthreads inline functions. +   Copyright (C) 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#define PT_EI + +#include <pt-machine.h> diff --git a/libpthread/linuxthreads/ptfork.c b/libpthread/linuxthreads/ptfork.c new file mode 100644 index 000000000..0c4e252d6 --- /dev/null +++ b/libpthread/linuxthreads/ptfork.c @@ -0,0 +1,107 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* mods for uClibc: removed strong alias and defined funcs properly */ + +/* The "atfork" stuff */ + +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include "pthread.h" +#include "internals.h" + +struct handler_list { +  void (*handler)(void); +  struct handler_list * next; +}; + +static pthread_mutex_t pthread_atfork_lock = PTHREAD_MUTEX_INITIALIZER; +static struct handler_list * pthread_atfork_prepare = NULL; +static struct handler_list * pthread_atfork_parent = NULL; +static struct handler_list * pthread_atfork_child = NULL; + +static void pthread_insert_list(struct handler_list ** list, +                                void (*handler)(void), +                                struct handler_list * newlist, +                                int at_end) +{ +  if (handler == NULL) return; +  if (at_end) { +    while(*list != NULL) list = &((*list)->next); +  } +  newlist->handler = handler; +  newlist->next = *list; +  *list = newlist; +} + +struct handler_list_block { +  struct handler_list prepare, parent, child; +}; + +int pthread_atfork(void (*prepare)(void), +		     void (*parent)(void), +		     void (*child)(void)) +{ +  struct handler_list_block * block = +    (struct handler_list_block *) malloc(sizeof(struct handler_list_block)); +  if (block == NULL) return ENOMEM; +  pthread_mutex_lock(&pthread_atfork_lock); +  /* "prepare" handlers are called in LIFO */ +  pthread_insert_list(&pthread_atfork_prepare, prepare, &block->prepare, 0); +  /* "parent" handlers are called in FIFO */ +  pthread_insert_list(&pthread_atfork_parent, parent, &block->parent, 1); +  /* "child" handlers are called in FIFO */ +  pthread_insert_list(&pthread_atfork_child, child, &block->child, 1); +  pthread_mutex_unlock(&pthread_atfork_lock); +  return 0; +} +//strong_alias (__pthread_atfork, pthread_atfork) + +static inline void pthread_call_handlers(struct handler_list * list) +{ +  for (/*nothing*/; list != NULL; list = list->next) (list->handler)(); +} + +extern int __libc_fork(void); + +pid_t __fork(void) +{ +  pid_t pid; +  struct handler_list * prepare, * child, * parent; + +  pthread_mutex_lock(&pthread_atfork_lock); +  prepare = pthread_atfork_prepare; +  child = pthread_atfork_child; +  parent = pthread_atfork_parent; +  pthread_mutex_unlock(&pthread_atfork_lock); +  pthread_call_handlers(prepare); +  pid = __libc_fork(); +  if (pid == 0) { +    __pthread_reset_main_thread(); +    __fresetlockfiles(); +    pthread_call_handlers(child); +  } else { +    pthread_call_handlers(parent); +  } +  return pid; +} +weak_alias (__fork, fork); + +pid_t __vfork(void) +{ +  return __fork(); +} +weak_alias (__vfork, vfork); diff --git a/libpthread/linuxthreads/pthread.c b/libpthread/linuxthreads/pthread.c new file mode 100644 index 000000000..51d600a18 --- /dev/null +++ b/libpthread/linuxthreads/pthread.c @@ -0,0 +1,876 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Thread creation, initialization, and basic low-level routines */ + +#define __FORCE_GLIBC +#include <features.h> +#include <errno.h> +#include <netdb.h>	/* for h_errno */ +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" +#include "debug.h"      /* added to linuxthreads -StS */ + +/* mods for uClibc: getpwd and getpagesize are the syscalls */ +#define __getpid getpid +#define __getpagesize getpagesize +/* mods for uClibc: __libc_sigaction is not in any standard headers */ +#include <signal.h> +extern int __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact); + + +/* These variables are used by the setup code.  */ +extern int _errno; +extern int _h_errno; + + +/* Descriptor of the initial thread */ + +struct _pthread_descr_struct __pthread_initial_thread = { +  &__pthread_initial_thread,  /* pthread_descr p_nextlive */ +  &__pthread_initial_thread,  /* pthread_descr p_prevlive */ +  NULL,                       /* pthread_descr p_nextwaiting */ +  NULL,			      /* pthread_descr p_nextlock */ +  PTHREAD_THREADS_MAX,        /* pthread_t p_tid */ +  0,                          /* int p_pid */ +  0,                          /* int p_priority */ +  &__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */ +  0,                          /* int p_signal */ +  NULL,                       /* sigjmp_buf * p_signal_buf */ +  NULL,                       /* sigjmp_buf * p_cancel_buf */ +  0,                          /* char p_terminated */ +  0,                          /* char p_detached */ +  0,                          /* char p_exited */ +  NULL,                       /* void * p_retval */ +  0,                          /* int p_retval */ +  NULL,                       /* pthread_descr p_joining */ +  NULL,                       /* struct _pthread_cleanup_buffer * p_cleanup */ +  0,                          /* char p_cancelstate */ +  0,                          /* char p_canceltype */ +  0,                          /* char p_canceled */ +  &_errno,                       /* int *p_errnop */ +  0,                          /* int p_errno */ +  &_h_errno,                       /* int *p_h_errnop */ +  0,                          /* int p_h_errno */ +  NULL,                       /* char * p_in_sighandler */ +  0,                          /* char p_sigwaiting */ +  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */ +  {NULL},                     /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */ +  {NULL},                     /* void * p_libc_specific[_LIBC_TSD_KEY_N] */ +  0,                          /* int p_userstack */ +  NULL,                       /* void * p_guardaddr */ +  0,                          /* size_t p_guardsize */ +  &__pthread_initial_thread,  /* pthread_descr p_self */ +  0,                          /* Always index 0 */ +  0,                          /* int p_report_events */ +  {{{0, }}, 0, NULL},         /* td_eventbuf_t p_eventbuf */ +  ATOMIC_INITIALIZER,         /* struct pthread_atomic p_resume_count */ +  0,                          /* char p_woken_by_cancel */ +  NULL,                       /* struct pthread_extricate_if *p_extricate */ +  NULL,	                      /* pthread_readlock_info *p_readlock_list; */ +  NULL,                       /* pthread_readlock_info *p_readlock_free; */ +  0                           /* int p_untracked_readlock_count; */ +}; + +/* Descriptor of the manager thread; none of this is used but the error +   variables, the p_pid and p_priority fields, +   and the address for identification.  */ + +struct _pthread_descr_struct __pthread_manager_thread = { +  NULL,                       /* pthread_descr p_nextlive */ +  NULL,                       /* pthread_descr p_prevlive */ +  NULL,                       /* pthread_descr p_nextwaiting */ +  NULL,			      /* pthread_descr p_nextlock */ +  0,                          /* int p_tid */ +  0,                          /* int p_pid */ +  0,                          /* int p_priority */ +  &__pthread_handles[1].h_lock, /* struct _pthread_fastlock * p_lock */ +  0,                          /* int p_signal */ +  NULL,                       /* sigjmp_buf * p_signal_buf */ +  NULL,                       /* sigjmp_buf * p_cancel_buf */ +  0,                          /* char p_terminated */ +  0,                          /* char p_detached */ +  0,                          /* char p_exited */ +  NULL,                       /* void * p_retval */ +  0,                          /* int p_retval */ +  NULL,                       /* pthread_descr p_joining */ +  NULL,                       /* struct _pthread_cleanup_buffer * p_cleanup */ +  0,                          /* char p_cancelstate */ +  0,                          /* char p_canceltype */ +  0,                          /* char p_canceled */ +  &__pthread_manager_thread.p_errno, /* int *p_errnop */ +  0,                          /* int p_errno */ +  NULL,                       /* int *p_h_errnop */ +  0,                          /* int p_h_errno */ +  NULL,                       /* char * p_in_sighandler */ +  0,                          /* char p_sigwaiting */ +  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */ +  {NULL},                     /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */ +  {NULL},                     /* void * p_libc_specific[_LIBC_TSD_KEY_N] */ +  0,                          /* int p_userstack */ +  NULL,                       /* void * p_guardaddr */ +  0,                          /* size_t p_guardsize */ +  &__pthread_manager_thread,  /* pthread_descr p_self */ +  1,                          /* Always index 1 */ +  0,                          /* int p_report_events */ +  {{{0, }}, 0, NULL},         /* td_eventbuf_t p_eventbuf */ +  ATOMIC_INITIALIZER,         /* struct pthread_atomic p_resume_count */ +  0,                          /* char p_woken_by_cancel */ +  NULL,                       /* struct pthread_extricate_if *p_extricate */ +  NULL,	                      /* pthread_readlock_info *p_readlock_list; */ +  NULL,                       /* pthread_readlock_info *p_readlock_free; */ +  0                           /* int p_untracked_readlock_count; */ +}; + +/* Pointer to the main thread (the father of the thread manager thread) */ +/* Originally, this is the initial thread, but this changes after fork() */ + +pthread_descr __pthread_main_thread = &__pthread_initial_thread; + +/* Limit between the stack of the initial thread (above) and the +   stacks of other threads (below). Aligned on a STACK_SIZE boundary. */ + +char *__pthread_initial_thread_bos = NULL; + +/* For non-MMU systems also remember to stack top of the initial thread. + * This is adapted when other stacks are malloc'ed since we don't know + * the bounds a-priori. -StS */ + +#ifndef __UCLIBC_HAS_MMU__ +char *__pthread_initial_thread_tos = NULL; +#endif /* __UCLIBC_HAS_MMU__ */ + +/* File descriptor for sending requests to the thread manager. */ +/* Initially -1, meaning that the thread manager is not running. */ + +int __pthread_manager_request = -1; + +/* Other end of the pipe for sending requests to the thread manager. */ + +int __pthread_manager_reader; + +/* Limits of the thread manager stack */ + +char *__pthread_manager_thread_bos = NULL; +char *__pthread_manager_thread_tos = NULL; + +/* For process-wide exit() */ + +int __pthread_exit_requested = 0; +int __pthread_exit_code = 0; + +/* Pointers that select new or old suspend/resume functions +   based on availability of rt signals. */ + +void (*__pthread_restart)(pthread_descr) = __pthread_restart_old; +void (*__pthread_suspend)(pthread_descr) = __pthread_suspend_old; + +/* Communicate relevant LinuxThreads constants to gdb */ + +const int __pthread_threads_max = PTHREAD_THREADS_MAX; +const int __pthread_sizeof_handle = sizeof(struct pthread_handle_struct); +const int __pthread_offsetof_descr = offsetof(struct pthread_handle_struct, +                                              h_descr); +const int __pthread_offsetof_pid = offsetof(struct _pthread_descr_struct, +                                            p_pid); + +/* Forward declarations */ + +static void pthread_exit_process(int retcode, void *arg); +#ifndef __i386__ +static void pthread_handle_sigcancel(int sig); +static void pthread_handle_sigrestart(int sig); +#else +static void pthread_handle_sigcancel(int sig, struct sigcontext ctx); +static void pthread_handle_sigrestart(int sig, struct sigcontext ctx); +#endif +static void pthread_handle_sigdebug(int sig); + +/* Signal numbers used for the communication. +   In these variables we keep track of the used variables.  If the +   platform does not support any real-time signals we will define the +   values to some unreasonable value which will signal failing of all +   the functions below.  */ +#ifndef __SIGRTMIN +static int current_rtmin = -1; +static int current_rtmax = -1; +int __pthread_sig_restart = SIGUSR1; +int __pthread_sig_cancel = SIGUSR2; +int __pthread_sig_debug = 0; +#else +static int current_rtmin; +static int current_rtmax; + +#if __SIGRTMAX - __SIGRTMIN >= 3 +int __pthread_sig_restart = __SIGRTMIN; +int __pthread_sig_cancel = __SIGRTMIN + 1; +int __pthread_sig_debug = __SIGRTMIN + 2; +#else +int __pthread_sig_restart = SIGUSR1; +int __pthread_sig_cancel = SIGUSR2; +int __pthread_sig_debug = 0; +#endif + +static int rtsigs_initialized; + +#include "testrtsig.h" + +static void +init_rtsigs (void) +{ +  if (!kernel_has_rtsig ()) +    { +      current_rtmin = -1; +      current_rtmax = -1; +#if __SIGRTMAX - __SIGRTMIN >= 3 +      __pthread_sig_restart = SIGUSR1; +      __pthread_sig_cancel = SIGUSR2; +      __pthread_sig_debug = 0; +#endif +PDEBUG("no rt-sigs, sig_restart=%d, sig_cancel=%d.\n", __pthread_sig_restart, __pthread_sig_cancel  ); +      __pthread_init_condvar(0); +    } +  else +    { +#if __SIGRTMAX - __SIGRTMIN >= 3 +      current_rtmin = __SIGRTMIN + 3; +      __pthread_restart = __pthread_restart_new; +      __pthread_suspend = __pthread_wait_for_restart_signal; +      __pthread_init_condvar(1); +#else +      current_rtmin = __SIGRTMIN; +      __pthread_init_condvar(0); +#endif + +      current_rtmax = __SIGRTMAX; +PDEBUG("have rt-sigs, rtmin = %d, rtmax = %d.\n", current_rtmin, current_rtmax); +    } + +  rtsigs_initialized = 1; +} +#endif + +/* Return number of available real-time signal with highest priority.  */ +int +__libc_current_sigrtmin (void) +{ +#ifdef __SIGRTMIN +  if (!rtsigs_initialized) +    init_rtsigs (); +#endif +  return current_rtmin; +} + +/* Return number of available real-time signal with lowest priority.  */ +int +__libc_current_sigrtmax (void) +{ +#ifdef __SIGRTMIN +  if (!rtsigs_initialized) +    init_rtsigs (); +#endif +  return current_rtmax; +} + +/* Allocate real-time signal with highest/lowest available +   priority.  Please note that we don't use a lock since we assume +   this function to be called at program start.  */ +int +__libc_allocate_rtsig (int high) +{ +#ifndef __SIGRTMIN +  return -1; +#else +  if (!rtsigs_initialized) +    init_rtsigs (); +  if (current_rtmin == -1 || current_rtmin > current_rtmax) +    /* We don't have anymore signal available.  */ +    return -1; + +  return high ? current_rtmin++ : current_rtmax--; +#endif +} + +/* Initialize the pthread library. +   Initialization is split in two functions: +   - a constructor function that blocks the __pthread_sig_restart signal +     (must do this very early, since the program could capture the signal +      mask with e.g. sigsetjmp before creating the first thread); +   - a regular function called from pthread_create when needed. */ + +static void pthread_initialize(void) __attribute__((constructor)); + +static void pthread_initialize(void) +{ +  struct sigaction sa; +  sigset_t mask; +  struct rlimit limit; +  int max_stack; + +  /* If already done (e.g. by a constructor called earlier!), bail out */ +  if (__pthread_initial_thread_bos != NULL) return; +#ifdef TEST_FOR_COMPARE_AND_SWAP +  /* Test if compare-and-swap is available */ +  __pthread_has_cas = compare_and_swap_is_available(); +#endif +  /* For the initial stack, reserve at least STACK_SIZE bytes of stack +     below the current stack address, and align that on a +     STACK_SIZE boundary. */ +  __pthread_initial_thread_bos = +    (char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1)); +  /* Update the descriptor for the initial thread. */ +  __pthread_initial_thread.p_pid = __getpid(); +  /* If we have special thread_self processing, initialize that for the +     main thread now.  */ +#ifdef INIT_THREAD_SELF +  INIT_THREAD_SELF(&__pthread_initial_thread, 0); +#endif +  /* The errno/h_errno variable of the main thread are the global ones.  */ +  __pthread_initial_thread.p_errnop = &_errno; +  __pthread_initial_thread.p_h_errnop = &_h_errno; +  /* Play with the stack size limit to make sure that no stack ever grows +     beyond STACK_SIZE minus two pages (one page for the thread descriptor +     immediately beyond, and one page to act as a guard page). */ + +#ifdef __UCLIBC_HAS_MMU__ +  /* We cannot allocate a huge chunk of memory to mmap all thread stacks later +   * on a non-MMU system. Thus, we don't need the rlimit either. -StS */ +  getrlimit(RLIMIT_STACK, &limit); +  max_stack = STACK_SIZE - 2 * __getpagesize(); +  if (limit.rlim_cur > max_stack) { +    limit.rlim_cur = max_stack; +    setrlimit(RLIMIT_STACK, &limit); +  } +#else +  /* For non-MMU assume __pthread_initial_thread_tos at upper page boundary, and +   * __pthread_initial_thread_bos at address 0. These bounds are refined as we  +   * malloc other stack frames such that they don't overlap. -StS +   */ +  __pthread_initial_thread_tos = +    (char *)(((long)CURRENT_STACK_FRAME + __getpagesize()) & ~(__getpagesize() - 1)); +  __pthread_initial_thread_bos = (char *) 1; /* set it non-zero so we know we have been here */ +  PDEBUG("initial thread stack bounds: bos=%p, tos=%p\n", +	 __pthread_initial_thread_bos, __pthread_initial_thread_tos); +#endif /* __UCLIBC_HAS_MMU__ */ + +#ifdef __SIGRTMIN +  /* Initialize real-time signals. */ +  init_rtsigs (); +#endif +  /* Setup signal handlers for the initial thread. +     Since signal handlers are shared between threads, these settings +     will be inherited by all other threads. */ +#ifndef __i386__ +  sa.sa_handler = pthread_handle_sigrestart; +#else +  sa.sa_handler = (__sighandler_t) pthread_handle_sigrestart; +#endif +  sigemptyset(&sa.sa_mask); +  sa.sa_flags = 0; +  __libc_sigaction(__pthread_sig_restart, &sa, NULL); +#ifndef __i386__ +  sa.sa_handler = pthread_handle_sigcancel; +#else +  sa.sa_handler = (__sighandler_t) pthread_handle_sigcancel; +#endif +  sa.sa_flags = 0; +  __libc_sigaction(__pthread_sig_cancel, &sa, NULL); +  if (__pthread_sig_debug > 0) { +    sa.sa_handler = pthread_handle_sigdebug; +    sigemptyset(&sa.sa_mask); +    sa.sa_flags = 0; +    __libc_sigaction(__pthread_sig_debug, &sa, NULL); +  } +  /* Initially, block __pthread_sig_restart. Will be unblocked on demand. */ +  sigemptyset(&mask); +  sigaddset(&mask, __pthread_sig_restart); +PDEBUG("block mask = %x\n", mask); +  sigprocmask(SIG_BLOCK, &mask, NULL); +  /* Register an exit function to kill all other threads. */ +  /* Do it early so that user-registered atexit functions are called +     before pthread_exit_process. */ +  on_exit(pthread_exit_process, NULL); +} + +void __pthread_initialize(void) +{ +  pthread_initialize(); +} + +int __pthread_initialize_manager(void) +{ +  int manager_pipe[2]; +  int pid; +  struct pthread_request request; + +  /* If basic initialization not done yet (e.g. we're called from a +     constructor run before our constructor), do it now */ +  if (__pthread_initial_thread_bos == NULL) pthread_initialize(); +  /* Setup stack for thread manager */ +  __pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE); +  if (__pthread_manager_thread_bos == NULL) return -1; +  __pthread_manager_thread_tos = +    __pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE; + +  /* On non-MMU systems we make sure that the initial thread bounds don't overlap +   * with the manager stack frame */ +  NOMMU_INITIAL_THREAD_BOUNDS(__pthread_manager_thread_tos,__pthread_manager_thread_bos); +  PDEBUG("manager stack: size=%d, bos=%p, tos=%p\n", THREAD_MANAGER_STACK_SIZE, +	 __pthread_manager_thread_bos, __pthread_manager_thread_tos); +  PDEBUG("initial stack: estimate bos=%p, tos=%p\n", +	 __pthread_initial_thread_bos, __pthread_initial_thread_tos); + +  /* Setup pipe to communicate with thread manager */ +  if (pipe(manager_pipe) == -1) { +    free(__pthread_manager_thread_bos); +    return -1; +  } +  /* Start the thread manager */ +  pid = 0; +  if (__pthread_initial_thread.p_report_events) +    { +      /* It's a bit more complicated.  We have to report the creation of +	 the manager thread.  */ +      int idx = __td_eventword (TD_CREATE); +      uint32_t mask = __td_eventmask (TD_CREATE); + +      if ((mask & (__pthread_threads_events.event_bits[idx] +		   | __pthread_initial_thread.p_eventbuf.eventmask.event_bits[idx])) +	  != 0) +	{ +	  pid = clone(__pthread_manager_event, +			(void **) __pthread_manager_thread_tos, +			CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, +			(void *)(long)manager_pipe[0]); + +	  if (pid != -1) +	    { +	      /* Now fill in the information about the new thread in +	         the newly created thread's data structure.  We cannot let +	         the new thread do this since we don't know whether it was +	         already scheduled when we send the event.  */ +	      __pthread_manager_thread.p_eventbuf.eventdata = +		&__pthread_manager_thread; +	      __pthread_manager_thread.p_eventbuf.eventnum = TD_CREATE; +	      __pthread_last_event = &__pthread_manager_thread; +	      __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; +	      __pthread_manager_thread.p_pid = pid; + +	      /* Now call the function which signals the event.  */ +	      __linuxthreads_create_event (); + +	      /* Now restart the thread.  */ +	      __pthread_unlock(__pthread_manager_thread.p_lock); +	    } +	} +    } + +  if (pid == 0) { +    pid = clone(__pthread_manager, (void **) __pthread_manager_thread_tos, +		  CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, +		  (void *)(long)manager_pipe[0]); +  } +  if (pid == -1) { +    free(__pthread_manager_thread_bos); +    __libc_close(manager_pipe[0]); +    __libc_close(manager_pipe[1]); +    return -1; +  } +  __pthread_manager_request = manager_pipe[1]; /* writing end */ +  __pthread_manager_reader = manager_pipe[0]; /* reading end */ +  __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; +  __pthread_manager_thread.p_pid = pid; +  /* Make gdb aware of new thread manager */ +  if (__pthread_threads_debug && __pthread_sig_debug > 0) +    { +      raise(__pthread_sig_debug); +      /* We suspend ourself and gdb will wake us up when it is +	 ready to handle us. */ +      __pthread_wait_for_restart_signal(thread_self()); +    } +  /* Synchronize debugging of the thread manager */ +PDEBUG("send REQ_DEBUG to manager thread\n"); +  request.req_kind = REQ_DEBUG; +  __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); +  return 0; +} + +/* Thread creation */ + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, +			 void * (*start_routine)(void *), void *arg) +{ +  pthread_descr self = thread_self(); +  struct pthread_request request; +  if (__pthread_manager_request < 0) { +    if (__pthread_initialize_manager() < 0) return EAGAIN; +  } +  request.req_thread = self; +  request.req_kind = REQ_CREATE; +  request.req_args.create.attr = attr; +  request.req_args.create.fn = start_routine; +  request.req_args.create.arg = arg; +  sigprocmask(SIG_SETMASK, (const sigset_t *) NULL, +              &request.req_args.create.mask); +  PDEBUG("write REQ_CREATE to manager thread\n"); +  __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); +PDEBUG("before suspend(self)\n"); +  suspend(self); +PDEBUG("after suspend(self)\n"); +  if (THREAD_GETMEM(self, p_retcode) == 0) +    *thread = (pthread_t) THREAD_GETMEM(self, p_retval); +  return THREAD_GETMEM(self, p_retcode); +} + +/* Simple operations on thread identifiers */ + +pthread_t pthread_self(void) +{ +  pthread_descr self = thread_self(); +  return THREAD_GETMEM(self, p_tid); +} + +int pthread_equal(pthread_t thread1, pthread_t thread2) +{ +  return thread1 == thread2; +} + +/* Helper function for thread_self in the case of user-provided stacks */ + +#ifndef THREAD_SELF + +pthread_descr __pthread_find_self() +{ +  char * sp = CURRENT_STACK_FRAME; +  pthread_handle h; + +  /* __pthread_handles[0] is the initial thread, __pthread_handles[1] is +     the manager threads handled specially in thread_self(), so start at 2 */ +  h = __pthread_handles + 2; +  while (! (sp <= (char *) h->h_descr && sp >= h->h_bottom)) h++; + +#ifdef DEBUG_PT +  if (h->h_descr == NULL) { +      printf("*** "__FUNCTION__" ERROR descriptor is NULL!!!!! ***\n\n"); +      _exit(1); +  } +#endif + +  return h->h_descr; +} + +#endif + +/* Thread scheduling */ + +int pthread_setschedparam(pthread_t thread, int policy, +                          const struct sched_param *param) +{ +  pthread_handle handle = thread_handle(thread); +  pthread_descr th; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, thread)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } +  th = handle->h_descr; +  if (sched_setscheduler(th->p_pid, policy, param) == -1) { +    __pthread_unlock(&handle->h_lock); +    return errno; +  } +  th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority; +  __pthread_unlock(&handle->h_lock); +  if (__pthread_manager_request >= 0) +    __pthread_manager_adjust_prio(th->p_priority); +  return 0; +} + +int pthread_getschedparam(pthread_t thread, int *policy, +                          struct sched_param *param) +{ +  pthread_handle handle = thread_handle(thread); +  int pid, pol; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, thread)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } +  pid = handle->h_descr->p_pid; +  __pthread_unlock(&handle->h_lock); +  pol = sched_getscheduler(pid); +  if (pol == -1) return errno; +  if (sched_getparam(pid, param) == -1) return errno; +  *policy = pol; +  return 0; +} + +/* Process-wide exit() request */ + +static void pthread_exit_process(int retcode, void *arg) +{ +  struct pthread_request request; +  pthread_descr self = thread_self(); + +  if (__pthread_manager_request >= 0) { +    request.req_thread = self; +    request.req_kind = REQ_PROCESS_EXIT; +    request.req_args.exit.code = retcode; +    __libc_write(__pthread_manager_request, +		 (char *) &request, sizeof(request)); +    suspend(self); +    /* Main thread should accumulate times for thread manager and its +       children, so that timings for main thread account for all threads. */ +    if (self == __pthread_main_thread) +      waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE); +  } +} + +/* The handler for the RESTART signal just records the signal received +   in the thread descriptor, and optionally performs a siglongjmp +   (for pthread_cond_timedwait). */ + +#ifndef __i386__ +static void pthread_handle_sigrestart(int sig) +{ +  pthread_descr self = thread_self(); +  PDEBUG("got called in non-i386 mode for %u\n", self); +#else +static void pthread_handle_sigrestart(int sig, struct sigcontext ctx) +{ +  pthread_descr self; +  asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs)); +  self = thread_self(); +  PDEBUG("got called in i386-mode for %u\n", self); +#endif +  THREAD_SETMEM(self, p_signal, sig); +  if (THREAD_GETMEM(self, p_signal_jmp) != NULL) +    siglongjmp(*THREAD_GETMEM(self, p_signal_jmp), 1); +} + +/* The handler for the CANCEL signal checks for cancellation +   (in asynchronous mode), for process-wide exit and exec requests. +   For the thread manager thread, redirect the signal to +   __pthread_manager_sighandler. */ + +#ifndef __i386__ +static void pthread_handle_sigcancel(int sig) +{ +  pthread_descr self = thread_self(); +  sigjmp_buf * jmpbuf; +#else +static void pthread_handle_sigcancel(int sig, struct sigcontext ctx) +{ +  pthread_descr self; +  sigjmp_buf * jmpbuf; +  asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs)); +  self = thread_self(); +#endif + +  if (self == &__pthread_manager_thread) +    { +      __pthread_manager_sighandler(sig); +      return; +    } +  if (__pthread_exit_requested) { +    /* Main thread should accumulate times for thread manager and its +       children, so that timings for main thread account for all threads. */ +    if (self == __pthread_main_thread) +      waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE); +    _exit(__pthread_exit_code); +  } +  if (THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +    if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) +      pthread_exit(PTHREAD_CANCELED); +    jmpbuf = THREAD_GETMEM(self, p_cancel_jmp); +    if (jmpbuf != NULL) { +      THREAD_SETMEM(self, p_cancel_jmp, NULL); +      siglongjmp(*jmpbuf, 1); +    } +  } +} + +/* Handler for the DEBUG signal. +   The debugging strategy is as follows: +   On reception of a REQ_DEBUG request (sent by new threads created to +   the thread manager under debugging mode), the thread manager throws +   __pthread_sig_debug to itself. The debugger (if active) intercepts +   this signal, takes into account new threads and continue execution +   of the thread manager by propagating the signal because it doesn't +   know what it is specifically done for. In the current implementation, +   the thread manager simply discards it. */ + +static void pthread_handle_sigdebug(int sig) +{ +  /* Nothing */ +} + +/* Reset the state of the thread machinery after a fork(). +   Close the pipe used for requests and set the main thread to the forked +   thread. +   Notice that we can't free the stack segments, as the forked thread +   may hold pointers into them. */ + +void __pthread_reset_main_thread() +{ +  pthread_descr self = thread_self(); + +  if (__pthread_manager_request != -1) { +    /* Free the thread manager stack */ +    free(__pthread_manager_thread_bos); +    __pthread_manager_thread_bos = __pthread_manager_thread_tos = NULL; +    /* Close the two ends of the pipe */ +    __libc_close(__pthread_manager_request); +    __libc_close(__pthread_manager_reader); +    __pthread_manager_request = __pthread_manager_reader = -1; +  } + +  /* Update the pid of the main thread */ +  THREAD_SETMEM(self, p_pid, __getpid()); +  /* Make the forked thread the main thread */ +  __pthread_main_thread = self; +  THREAD_SETMEM(self, p_nextlive, self); +  THREAD_SETMEM(self, p_prevlive, self); +  /* Now this thread modifies the global variables.  */ +  THREAD_SETMEM(self, p_errnop, &_errno); +  THREAD_SETMEM(self, p_h_errnop, &_h_errno); +} + +/* Process-wide exec() request */ + +void __pthread_kill_other_threads_np(void) +{ +  struct sigaction sa; +  /* Terminate all other threads and thread manager */ +  pthread_exit_process(0, NULL); +  /* Make current thread the main thread in case the calling thread +     changes its mind, does not exec(), and creates new threads instead. */ +  __pthread_reset_main_thread(); +  /* Reset the signal handlers behaviour for the signals the +     implementation uses since this would be passed to the new +     process.  */ +  sigemptyset(&sa.sa_mask); +  sa.sa_flags = 0; +  sa.sa_handler = SIG_DFL; +  __libc_sigaction(__pthread_sig_restart, &sa, NULL); +  __libc_sigaction(__pthread_sig_cancel, &sa, NULL); +  if (__pthread_sig_debug > 0) +    __libc_sigaction(__pthread_sig_debug, &sa, NULL); +} +weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np) + +/* Concurrency symbol level.  */ +static int current_level; + +int __pthread_setconcurrency(int level) +{ +  /* We don't do anything unless we have found a useful interpretation.  */ +  current_level = level; +  return 0; +} +weak_alias (__pthread_setconcurrency, pthread_setconcurrency) + +int __pthread_getconcurrency(void) +{ +  return current_level; +} +weak_alias (__pthread_getconcurrency, pthread_getconcurrency) + +void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif) +{ +  __pthread_lock(self->p_lock, self); +  THREAD_SETMEM(self, p_extricate, peif); +  __pthread_unlock(self->p_lock); +} + +/* Primitives for controlling thread execution */ + +void __pthread_wait_for_restart_signal(pthread_descr self) +{ +  sigset_t mask; + +  sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */ +  sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */ +  do { +    self->p_signal = 0; +    PDEBUG("temporary block mask = %x\n", mask); +    sigsuspend(&mask);                   /* Wait for signal */ +    PDEBUG(" *** after sigsuspend *** \n"); +  } while (self->p_signal !=__pthread_sig_restart ); +} + +/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT signals. +   On these kernels, we use SIGUSR1 and SIGUSR2 for restart and cancellation. +   Since the restart signal does not queue, we use an atomic counter to create +   queuing semantics. This is needed to resolve a rare race condition in +   pthread_cond_timedwait_relative. */ + +void __pthread_restart_old(pthread_descr th) +{ +  if (atomic_increment(&th->p_resume_count) == -1) +    kill(th->p_pid, __pthread_sig_restart); +} + +void __pthread_suspend_old(pthread_descr self) +{ +  if (atomic_decrement(&self->p_resume_count) <= 0) +    __pthread_wait_for_restart_signal(self); +} + +void __pthread_restart_new(pthread_descr th) +{ +    kill(th->p_pid, __pthread_sig_restart); +} + +/* There is no __pthread_suspend_new because it would just +   be a wasteful wrapper for __pthread_wait_for_restart_signal */ + +/* Debugging aid */ + +#ifdef DEBUG_PT +#include <stdarg.h> + +void __pthread_message(char * fmt, ...) +{ +  char buffer[1024]; +  va_list args; +  sprintf(buffer, "%05d : ", __getpid()); +  va_start(args, fmt); +  vsnprintf(buffer + 8, sizeof(buffer) - 8, fmt, args); +  va_end(args); +  __libc_write(2, buffer, strlen(buffer)); +} + +#endif + + +#ifndef PIC +/* We need a hook to force the cancelation wrappers to be linked in when +   static libpthread is used.  */ +extern const int __pthread_provide_wrappers; +static const int *const __pthread_require_wrappers = +  &__pthread_provide_wrappers; +#endif diff --git a/libpthread/linuxthreads/ptlongjmp.c b/libpthread/linuxthreads/ptlongjmp.c new file mode 100644 index 000000000..c0ea8223a --- /dev/null +++ b/libpthread/linuxthreads/ptlongjmp.c @@ -0,0 +1,55 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Redefine siglongjmp and longjmp so that they interact correctly +   with cleanup handlers */ + +#include <setjmp.h> +#include "pthread.h" +#include "internals.h" + +/* These functions are not declared anywhere since they shouldn't be +   used at another place but here.  */ +extern void __libc_siglongjmp (sigjmp_buf env, int val) +     __attribute__ ((noreturn)); +extern void __libc_longjmp (sigjmp_buf env, int val) +     __attribute__ ((noreturn)); + + +static void pthread_cleanup_upto(__jmp_buf target) +{ +  pthread_descr self = thread_self(); +  struct _pthread_cleanup_buffer * c; + +  for (c = THREAD_GETMEM(self, p_cleanup); +       c != NULL && _JMPBUF_UNWINDS(target, c); +       c = c->__prev) +    c->__routine(c->__arg); +  THREAD_SETMEM(self, p_cleanup, c); +  if (THREAD_GETMEM(self, p_in_sighandler) +      && _JMPBUF_UNWINDS(target, THREAD_GETMEM(self, p_in_sighandler))) +    THREAD_SETMEM(self, p_in_sighandler, NULL); +} + +void siglongjmp(sigjmp_buf env, int val) +{ +  pthread_cleanup_upto(env->__jmpbuf); +  __libc_siglongjmp(env, val); +} + +void longjmp(jmp_buf env, int val) +{ +  pthread_cleanup_upto(env->__jmpbuf); +  __libc_siglongjmp(env, val); +} diff --git a/libpthread/linuxthreads/queue.h b/libpthread/linuxthreads/queue.h new file mode 100644 index 000000000..28bd75531 --- /dev/null +++ b/libpthread/linuxthreads/queue.h @@ -0,0 +1,61 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Waiting queues */ + +/* Waiting queues are represented by lists of thread descriptors +   linked through their p_nextwaiting field.  The lists are kept +   sorted by decreasing priority, and then decreasing waiting time. */ + +static inline void enqueue(pthread_descr * q, pthread_descr th) +{ +  int prio = th->p_priority; +  ASSERT(th->p_nextwaiting == NULL); +  for (; *q != NULL; q = &((*q)->p_nextwaiting)) { +    if (prio > (*q)->p_priority) { +      th->p_nextwaiting = *q; +      *q = th; +      return; +    } +  } +  *q = th; +} + +static inline pthread_descr dequeue(pthread_descr * q) +{ +  pthread_descr th; +  th = *q; +  if (th != NULL) { +    *q = th->p_nextwaiting; +    th->p_nextwaiting = NULL; +  } +  return th; +} + +static inline int remove_from_queue(pthread_descr * q, pthread_descr th) +{ +  for (; *q != NULL; q = &((*q)->p_nextwaiting)) { +    if (*q == th) { +      *q = th->p_nextwaiting; +      th->p_nextwaiting = NULL; +      return 1; +    } +  } +  return 0; +} + +static inline int queue_is_empty(pthread_descr * q) +{ +    return *q == NULL; +} diff --git a/libpthread/linuxthreads/restart.h b/libpthread/linuxthreads/restart.h new file mode 100644 index 000000000..702d7d15c --- /dev/null +++ b/libpthread/linuxthreads/restart.h @@ -0,0 +1,27 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +#include <signal.h> + +/* Primitives for controlling thread execution */ + +static inline void restart(pthread_descr th) +{ +  __pthread_restart(th); /* see pthread.c */  +} + +static inline void suspend(pthread_descr self) +{ +  __pthread_suspend(self); /* see pthread.c */ +} diff --git a/libpthread/linuxthreads/rwlock.c b/libpthread/linuxthreads/rwlock.c new file mode 100644 index 000000000..977fd88af --- /dev/null +++ b/libpthread/linuxthreads/rwlock.c @@ -0,0 +1,486 @@ +/* Read-write lock implementation. +   Copyright (C) 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Xavier Leroy <Xavier.Leroy@inria.fr> +   and Ulrich Drepper <drepper@cygnus.com>, 1998. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include "internals.h" +#include "queue.h" +#include "spinlock.h" +#include "restart.h" + +/* + * Check whether the calling thread already owns one or more read locks on the + * specified lock. If so, return a pointer to the read lock info structure + * corresponding to that lock. + */ + +static pthread_readlock_info * +rwlock_is_in_list(pthread_descr self, pthread_rwlock_t *rwlock) +{ +  pthread_readlock_info *info; + +  for (info = self->p_readlock_list; info != NULL; info = info->pr_next) +    { +      if (info->pr_lock == rwlock) +	return info; +    } + +  return NULL; +} + +/* + * Add a new lock to the thread's list of locks for which it has a read lock. + * A new info node must be allocated for this, which is taken from the thread's + * free list, or by calling malloc. If malloc fails, a null pointer is + * returned. Otherwise the lock info structure is initialized and pushed + * onto the thread's list. + */ + +static pthread_readlock_info * +rwlock_add_to_list(pthread_descr self, pthread_rwlock_t *rwlock) +{ +  pthread_readlock_info *info = self->p_readlock_free; + +  if (info != NULL)  +    self->p_readlock_free = info->pr_next; +  else +    info = malloc(sizeof *info); + +  if (info == NULL) +    return NULL; + +  info->pr_lock_count = 1; +  info->pr_lock = rwlock; +  info->pr_next = self->p_readlock_list; +  self->p_readlock_list = info; + +  return info; +} + +/* + * If the thread owns a read lock over the given pthread_rwlock_t, + * and this read lock is tracked in the thread's lock list, + * this function returns a pointer to the info node in that list. + * It also decrements the lock count within that node, and if + * it reaches zero, it removes the node from the list. + * If nothing is found, it returns a null pointer. + */ + +static pthread_readlock_info * +rwlock_remove_from_list(pthread_descr self, pthread_rwlock_t *rwlock) +{ +  pthread_readlock_info **pinfo; + +  for (pinfo = &self->p_readlock_list; *pinfo != NULL; pinfo = &(*pinfo)->pr_next) +    { +      if ((*pinfo)->pr_lock == rwlock) +	{ +	  pthread_readlock_info *info = *pinfo; +	  if (--info->pr_lock_count == 0) +	    *pinfo = info->pr_next; +	  return info; +	} +    } +   +  return NULL; +} + +/* + * This function checks whether the conditions are right to place a read lock. + * It returns 1 if so, otherwise zero. The rwlock's internal lock must be + * locked upon entry. + */ + +static int +rwlock_can_rdlock(pthread_rwlock_t *rwlock, int have_lock_already) +{ +  /* Can't readlock; it is write locked. */ +  if (rwlock->__rw_writer != NULL) +    return 0; + +  /* Lock prefers readers; get it. */ +  if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP) +    return 1; + +  /* Lock prefers writers, but none are waiting. */ +  if (queue_is_empty(&rwlock->__rw_write_waiting)) +    return 1; + +  /* Writers are waiting, but this thread already has a read lock */ +  if (have_lock_already) +    return 1; + +  /* Writers are waiting, and this is a new lock */ +  return 0; +} + +/* + * This function helps support brain-damaged recursive read locking + * semantics required by Unix 98, while maintaining write priority. + * This basically determines whether this thread already holds a read lock + * already. It returns 1 if so, otherwise it returns 0.   + * + * If the thread has any ``untracked read locks'' then it just assumes + * that this lock is among them, just to be safe, and returns 1. + * + * Also, if it finds the thread's lock in the list, it sets the pointer + * referenced by pexisting to refer to the list entry. + * + * If the thread has no untracked locks, and the lock is not found + * in its list, then it is added to the list. If this fails, + * then *pout_of_mem is set to 1. + */ + +static int +rwlock_have_already(pthread_descr *pself, pthread_rwlock_t *rwlock, +    pthread_readlock_info **pexisting, int *pout_of_mem) +{ +  pthread_readlock_info *existing = NULL; +  int out_of_mem = 0, have_lock_already = 0; +  pthread_descr self = *pself; + +  if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_WRITER_NP) +    { +      if (!self) +	self = thread_self(); + +      existing = rwlock_is_in_list(self, rwlock); + +      if (existing != NULL || self->p_untracked_readlock_count > 0) +	have_lock_already = 1; +      else +	{ +	  existing = rwlock_add_to_list(self, rwlock); +	  if (existing == NULL) +	    out_of_mem = 1; +	} +    } + +  *pout_of_mem = out_of_mem; +  *pexisting = existing; +  *pself = self; + +  return have_lock_already; +} + +int +pthread_rwlock_init (pthread_rwlock_t *rwlock, +		     const pthread_rwlockattr_t *attr) +{ +  __pthread_init_lock(&rwlock->__rw_lock); +  rwlock->__rw_readers = 0; +  rwlock->__rw_writer = NULL; +  rwlock->__rw_read_waiting = NULL; +  rwlock->__rw_write_waiting = NULL; + +  if (attr == NULL) +    { +      rwlock->__rw_kind = PTHREAD_RWLOCK_DEFAULT_NP; +      rwlock->__rw_pshared = PTHREAD_PROCESS_PRIVATE; +    } +  else +    { +      rwlock->__rw_kind = attr->__lockkind; +      rwlock->__rw_pshared = attr->__pshared; +    } + +  return 0; +} + + +int +pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ +  int readers; +  _pthread_descr writer; + +  __pthread_lock (&rwlock->__rw_lock, NULL); +  readers = rwlock->__rw_readers; +  writer = rwlock->__rw_writer; +  __pthread_unlock (&rwlock->__rw_lock); + +  if (readers > 0 || writer != NULL) +    return EBUSY; + +  return 0; +} + +int +pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +{ +  pthread_descr self = NULL; +  pthread_readlock_info *existing; +  int out_of_mem, have_lock_already; + +  have_lock_already = rwlock_have_already(&self, rwlock, +      &existing, &out_of_mem); + +  for (;;) +    { +      if (self == NULL) +	self = thread_self (); + +      __pthread_lock (&rwlock->__rw_lock, self); + +      if (rwlock_can_rdlock(rwlock, have_lock_already)) +	break; + +      enqueue (&rwlock->__rw_read_waiting, self); +      __pthread_unlock (&rwlock->__rw_lock); +      suspend (self); /* This is not a cancellation point */ +    } + +  ++rwlock->__rw_readers; +  __pthread_unlock (&rwlock->__rw_lock); + +  if (have_lock_already || out_of_mem) +    { +      if (existing != NULL) +	existing->pr_lock_count++; +      else +	self->p_untracked_readlock_count++; +    } +   +  return 0; +} + +int +pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) +{ +  pthread_descr self = thread_self(); +  pthread_readlock_info *existing; +  int out_of_mem, have_lock_already; +  int retval = EBUSY; + +  have_lock_already = rwlock_have_already(&self, rwlock, +      &existing, &out_of_mem); + +  __pthread_lock (&rwlock->__rw_lock, self); + +  /* 0 is passed to here instead of have_lock_already. +     This is to meet Single Unix Spec requirements:  +     if writers are waiting, pthread_rwlock_tryrdlock +     does not acquire a read lock, even if the caller has +     one or more read locks already. */ + +  if (rwlock_can_rdlock(rwlock, 0)) +    { +      ++rwlock->__rw_readers; +      retval = 0; +    } + +  __pthread_unlock (&rwlock->__rw_lock); + +  if (retval == 0) +    { +      if (have_lock_already || out_of_mem) +	{ +	  if (existing != NULL) +	    existing->pr_lock_count++; +	  else +	    self->p_untracked_readlock_count++; +	} +    } +   +  return retval; +} + + +int +pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +{ +  pthread_descr self = thread_self (); + +  while(1) +    { +      __pthread_lock (&rwlock->__rw_lock, self); +      if (rwlock->__rw_readers == 0 && rwlock->__rw_writer == NULL) +	{ +	  rwlock->__rw_writer = self; +	  __pthread_unlock (&rwlock->__rw_lock); +	  return 0; +	} + +      /* Suspend ourselves, then try again */ +      enqueue (&rwlock->__rw_write_waiting, self); +      __pthread_unlock (&rwlock->__rw_lock); +      suspend (self); /* This is not a cancellation point */ +    } +} + + +int +pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) +{ +  int result = EBUSY; + +  __pthread_lock (&rwlock->__rw_lock, NULL); +  if (rwlock->__rw_readers == 0 && rwlock->__rw_writer == NULL) +    { +      rwlock->__rw_writer = thread_self (); +      result = 0; +    } +  __pthread_unlock (&rwlock->__rw_lock); + +  return result; +} + + +int +pthread_rwlock_unlock (pthread_rwlock_t *rwlock) +{ +  pthread_descr torestart; +  pthread_descr th; + +  __pthread_lock (&rwlock->__rw_lock, NULL); +  if (rwlock->__rw_writer != NULL) +    { +      /* Unlocking a write lock.  */ +      if (rwlock->__rw_writer != thread_self ()) +	{ +	  __pthread_unlock (&rwlock->__rw_lock); +	  return EPERM; +	} +      rwlock->__rw_writer = NULL; + +      if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP +	  || (th = dequeue (&rwlock->__rw_write_waiting)) == NULL) +	{ +	  /* Restart all waiting readers.  */ +	  torestart = rwlock->__rw_read_waiting; +	  rwlock->__rw_read_waiting = NULL; +	  __pthread_unlock (&rwlock->__rw_lock); +	  while ((th = dequeue (&torestart)) != NULL) +	    restart (th); +	} +      else +	{ +	  /* Restart one waiting writer.  */ +	  __pthread_unlock (&rwlock->__rw_lock); +	  restart (th); +	} +    } +  else +    { +      /* Unlocking a read lock.  */ +      if (rwlock->__rw_readers == 0) +	{ +	  __pthread_unlock (&rwlock->__rw_lock); +	  return EPERM; +	} + +      --rwlock->__rw_readers; +      if (rwlock->__rw_readers == 0) +	/* Restart one waiting writer, if any.  */ +	th = dequeue (&rwlock->__rw_write_waiting); +      else +	th = NULL; + +      __pthread_unlock (&rwlock->__rw_lock); +      if (th != NULL) +	restart (th); + +      /* Recursive lock fixup */ + +      if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_WRITER_NP) +	{ +	  pthread_descr self = thread_self(); +	  pthread_readlock_info *victim = rwlock_remove_from_list(self, rwlock); + +	  if (victim != NULL) +	    { +	      if (victim->pr_lock_count == 0) +		{ +		  victim->pr_next = self->p_readlock_free; +		  self->p_readlock_free = victim; +		} +	    } +	  else +	    { +	      if (self->p_untracked_readlock_count > 0) +		self->p_untracked_readlock_count--; +	    } +	} +    } + +  return 0; +} + + + +int +pthread_rwlockattr_init (pthread_rwlockattr_t *attr) +{ +  attr->__lockkind = 0; +  attr->__pshared = 0; + +  return 0; +} + + +int +pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr) +{ +  return 0; +} + + +int +pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared) +{ +  *pshared = attr->__pshared; +  return 0; +} + + +int +pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) +{ +  if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) +    return EINVAL; + +  attr->__pshared = pshared; + +  return 0; +} + + +int +pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t *attr, int *pref) +{ +  *pref = attr->__lockkind; +  return 0; +} + + +int +pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref) +{ +  if (pref != PTHREAD_RWLOCK_PREFER_READER_NP +      && pref != PTHREAD_RWLOCK_PREFER_WRITER_NP +      && pref != PTHREAD_RWLOCK_DEFAULT_NP) +    return EINVAL; + +  attr->__lockkind = pref; + +  return 0; +} diff --git a/libpthread/linuxthreads/semaphore.c b/libpthread/linuxthreads/semaphore.c new file mode 100644 index 000000000..0297b3a1e --- /dev/null +++ b/libpthread/linuxthreads/semaphore.c @@ -0,0 +1,209 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Semaphores a la POSIX 1003.1b */ + +#include <errno.h> +#include "pthread.h" +#include "semaphore.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" +#include "queue.h" + +int __new_sem_init(sem_t *sem, int pshared, unsigned int value) +{ +  if (value > SEM_VALUE_MAX) { +    errno = EINVAL; +    return -1; +  } +  if (pshared) { +    errno = ENOSYS; +    return -1; +  } +  __pthread_init_lock((struct _pthread_fastlock *) &sem->__sem_lock); +  sem->__sem_value = value; +  sem->__sem_waiting = NULL; +  return 0; +} + +/* Function called by pthread_cancel to remove the thread from +   waiting inside __new_sem_wait. */ + +static int new_sem_extricate_func(void *obj, pthread_descr th) +{ +  volatile pthread_descr self = thread_self(); +  sem_t *sem = obj; +  int did_remove = 0; + +  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self); +  did_remove = remove_from_queue(&sem->__sem_waiting, th); +  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); + +  return did_remove; +} + +int __new_sem_wait(sem_t * sem) +{ +  volatile pthread_descr self = thread_self(); +  pthread_extricate_if extr; +  int already_canceled = 0; + +  /* Set up extrication interface */ +  extr.pu_object = sem; +  extr.pu_extricate_func = new_sem_extricate_func; + +  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self); +  if (sem->__sem_value > 0) { +    sem->__sem_value--; +    __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); +    return 0; +  } +  /* Register extrication interface */ +  __pthread_set_own_extricate_if(self, &extr);  +  /* Enqueue only if not already cancelled. */ +  if (!(THREAD_GETMEM(self, p_canceled) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) +    enqueue(&sem->__sem_waiting, self); +  else +    already_canceled = 1; +  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); + +  if (already_canceled) { +    __pthread_set_own_extricate_if(self, 0);  +    pthread_exit(PTHREAD_CANCELED); +  }  + +  /* Wait for sem_post or cancellation, or fall through if already canceled */ +  suspend(self); +  __pthread_set_own_extricate_if(self, 0);  + +  /* Terminate only if the wakeup came from cancellation. */ +  /* Otherwise ignore cancellation because we got the semaphore. */ + +  if (THREAD_GETMEM(self, p_woken_by_cancel) +      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { +    THREAD_SETMEM(self, p_woken_by_cancel, 0); +    pthread_exit(PTHREAD_CANCELED); +  } +  /* We got the semaphore */ +  return 0; +} + +int __new_sem_trywait(sem_t * sem) +{ +  int retval; + +  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, NULL); +  if (sem->__sem_value == 0) { +    errno = EAGAIN; +    retval = -1; +  } else { +    sem->__sem_value--; +    retval = 0; +  } +  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); +  return retval; +} + +int __new_sem_post(sem_t * sem) +{ +  pthread_descr self = thread_self(); +  pthread_descr th; +  struct pthread_request request; + +  if (THREAD_GETMEM(self, p_in_sighandler) == NULL) { +    __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self); +    if (sem->__sem_waiting == NULL) { +      if (sem->__sem_value >= SEM_VALUE_MAX) { +        /* Overflow */ +        errno = ERANGE; +        __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); +        return -1; +      } +      sem->__sem_value++; +      __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); +    } else { +      th = dequeue(&sem->__sem_waiting); +      __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock); +      restart(th); +    } +  } else { +    /* If we're in signal handler, delegate post operation to +       the thread manager. */ +    if (__pthread_manager_request < 0) { +      if (__pthread_initialize_manager() < 0) { +        errno = EAGAIN; +        return -1; +      } +    } +    request.req_kind = REQ_POST; +    request.req_args.post = sem; +    __libc_write(__pthread_manager_request, +                 (char *) &request, sizeof(request)); +  } +  return 0; +} + +int __new_sem_getvalue(sem_t * sem, int * sval) +{ +  *sval = sem->__sem_value; +  return 0; +} + +int __new_sem_destroy(sem_t * sem) +{ +  if (sem->__sem_waiting != NULL) { +    __set_errno (EBUSY); +    return -1; +  } +  return 0; +} + +sem_t *sem_open(const char *name, int oflag, ...) +{ +  __set_errno (ENOSYS); +  return SEM_FAILED; +} + +int sem_close(sem_t *sem) +{ +  __set_errno (ENOSYS); +  return -1; +} + +int sem_unlink(const char *name) +{ +  __set_errno (ENOSYS); +  return -1; +} + +#if defined PIC && DO_VERSIONING +default_symbol_version (__new_sem_init, sem_init, GLIBC_2.1); +default_symbol_version (__new_sem_wait, sem_wait, GLIBC_2.1); +default_symbol_version (__new_sem_trywait, sem_trywait, GLIBC_2.1); +default_symbol_version (__new_sem_post, sem_post, GLIBC_2.1); +default_symbol_version (__new_sem_getvalue, sem_getvalue, GLIBC_2.1); +default_symbol_version (__new_sem_destroy, sem_destroy, GLIBC_2.1); +#else +# ifdef weak_alias +weak_alias (__new_sem_init, sem_init) +weak_alias (__new_sem_wait, sem_wait) +weak_alias (__new_sem_trywait, sem_trywait) +weak_alias (__new_sem_post, sem_post) +weak_alias (__new_sem_getvalue, sem_getvalue) +weak_alias (__new_sem_destroy, sem_destroy) +# endif +#endif +     diff --git a/libpthread/linuxthreads/semaphore.h b/libpthread/linuxthreads/semaphore.h new file mode 100644 index 000000000..84742233b --- /dev/null +++ b/libpthread/linuxthreads/semaphore.h @@ -0,0 +1,80 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +#ifndef _SEMAPHORE_H +#define _SEMAPHORE_H    1 + +#include <features.h> +#include <sys/types.h> + +#ifndef _PTHREAD_DESCR_DEFINED +/* Thread descriptors.  Needed for `sem_t' definition.  */ +typedef struct _pthread_descr_struct *_pthread_descr; +# define _PTHREAD_DESCR_DEFINED +#endif + +/* System specific semaphore definition.  */ +typedef struct +{ +  struct +  { +    long int status; +    int spinlock; +  } __sem_lock; +  int __sem_value; +  _pthread_descr __sem_waiting; +} sem_t; + + + +/* Value returned if `sem_open' failed.  */ +#define SEM_FAILED	((sem_t *) 0) + +/* Maximum value the semaphore can have.  */ +#define SEM_VALUE_MAX 	((int) ((~0u) >> 1)) + + +__BEGIN_DECLS + +/* Initialize semaphore object SEM to VALUE.  If PSHARED then share it +   with other processes.  */ +extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value)); + +/* Free resources associated with semaphore object SEM.  */ +extern int sem_destroy __P ((sem_t *__sem)); + +/* Open a named semaphore NAME with open flaot OFLAG.  */ +extern sem_t *sem_open __P ((__const char *__name, int __oflag, ...)); + +/* Close descriptor for named semaphore SEM.  */ +extern int sem_close __P ((sem_t *__sem)); + +/* Remove named semaphore NAME.  */ +extern int sem_unlink __P ((__const char *__name)); + +/* Wait for SEM being posted.  */ +extern int sem_wait __P ((sem_t *__sem)); + +/* Test whether SEM is posted.  */ +extern int sem_trywait __P ((sem_t *__sem)); + +/* Post SEM.  */ +extern int sem_post __P ((sem_t *__sem)); + +/* Get current value of SEM and store it in *SVAL.  */ +extern int sem_getvalue __P ((sem_t *__sem, int *__sval)); + +__END_DECLS + +#endif  /* semaphore.h */ diff --git a/libpthread/linuxthreads/signals.c b/libpthread/linuxthreads/signals.c new file mode 100644 index 000000000..de156e15a --- /dev/null +++ b/libpthread/linuxthreads/signals.c @@ -0,0 +1,239 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Handling of signals */ + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include <ucontext.h> +#include <sigcontextinfo.h> + +/* mods for uClibc: __libc_sigaction is not in any standard headers */ +extern int __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact); + +int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask) +{ +  sigset_t mask; + +  if (newmask != NULL) { +    mask = *newmask; +    /* Don't allow __pthread_sig_restart to be unmasked. +       Don't allow __pthread_sig_cancel to be masked. */ +    switch(how) { +    case SIG_SETMASK: +      sigaddset(&mask, __pthread_sig_restart); +      sigdelset(&mask, __pthread_sig_cancel); +      break; +    case SIG_BLOCK: +      sigdelset(&mask, __pthread_sig_cancel); +      break; +    case SIG_UNBLOCK: +      sigdelset(&mask, __pthread_sig_restart); +      break; +    } +    newmask = &mask; +  } +  if (sigprocmask(how, newmask, oldmask) == -1) +    return errno; +  else +    return 0; +} + +int pthread_kill(pthread_t thread, int signo) +{ +  pthread_handle handle = thread_handle(thread); +  int pid; + +  __pthread_lock(&handle->h_lock, NULL); +  if (invalid_handle(handle, thread)) { +    __pthread_unlock(&handle->h_lock); +    return ESRCH; +  } +  pid = handle->h_descr->p_pid; +  __pthread_unlock(&handle->h_lock); +  if (kill(pid, signo) == -1) +    return errno; +  else +    return 0; +} + +/* User-provided signal handlers */ +typedef void (*arch_sighandler_t) __PMT ((int, SIGCONTEXT)); +static union +{ +  arch_sighandler_t old; +  void (*rt) (int, struct siginfo *, struct ucontext *); +} sighandler[NSIG]; + +/* The wrapper around user-provided signal handlers */ +static void pthread_sighandler(int signo, SIGCONTEXT ctx) +{ +  pthread_descr self = thread_self(); +  char * in_sighandler; +  /* If we're in a sigwait operation, just record the signal received +     and return without calling the user's handler */ +  if (THREAD_GETMEM(self, p_sigwaiting)) { +    THREAD_SETMEM(self, p_sigwaiting, 0); +    THREAD_SETMEM(self, p_signal, signo); +    return; +  } +  /* Record that we're in a signal handler and call the user's +     handler function */ +  in_sighandler = THREAD_GETMEM(self, p_in_sighandler); +  if (in_sighandler == NULL) +    THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME); +  sighandler[signo].old(signo, SIGCONTEXT_EXTRA_ARGS ctx); +  if (in_sighandler == NULL) +    THREAD_SETMEM(self, p_in_sighandler, NULL); +} + +/* The same, this time for real-time signals.  */ +static void pthread_sighandler_rt(int signo, struct siginfo *si, +				  struct ucontext *uc) +{ +  pthread_descr self = thread_self(); +  char * in_sighandler; +  /* If we're in a sigwait operation, just record the signal received +     and return without calling the user's handler */ +  if (THREAD_GETMEM(self, p_sigwaiting)) { +    THREAD_SETMEM(self, p_sigwaiting, 0); +    THREAD_SETMEM(self, p_signal, signo); +    return; +  } +  /* Record that we're in a signal handler and call the user's +     handler function */ +  in_sighandler = THREAD_GETMEM(self, p_in_sighandler); +  if (in_sighandler == NULL) +    THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME); +  sighandler[signo].rt(signo, si, uc); +  if (in_sighandler == NULL) +    THREAD_SETMEM(self, p_in_sighandler, NULL); +} + +/* The wrapper around sigaction.  Install our own signal handler +   around the signal. */ +int sigaction(int sig, const struct sigaction * act, +              struct sigaction * oact) +{ +  struct sigaction newact; +  struct sigaction *newactp; + +printf(__FUNCTION__": pthreads wrapper!\n"); +  if (sig == __pthread_sig_restart || +      sig == __pthread_sig_cancel || +      (sig == __pthread_sig_debug && __pthread_sig_debug > 0)) +    return EINVAL; +  if (act) +    { +      newact = *act; +      if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL +	  && sig > 0 && sig < NSIG) +	{ +	  if (act->sa_flags & SA_SIGINFO) +	    newact.sa_handler = (__sighandler_t) pthread_sighandler_rt; +	  else +	    newact.sa_handler = (__sighandler_t) pthread_sighandler; +	} +      newactp = &newact; +    } +  else +    newactp = NULL; +  if (__libc_sigaction(sig, newactp, oact) == -1) +    return -1; +printf(__FUNCTION__": signahdler installed, __sigaction successful\n"); +  if (sig > 0 && sig < NSIG) +    { +      if (oact != NULL) +	oact->sa_handler = (__sighandler_t) sighandler[sig].old; +      if (act) +	/* For the assignment is does not matter whether it's a normal +	   or real-time signal.  */ +	sighandler[sig].old = (arch_sighandler_t) act->sa_handler; +    } +  return 0; +} + +/* A signal handler that does nothing */ +static void pthread_null_sighandler(int sig) { } + +/* sigwait -- synchronously wait for a signal */ +int sigwait(const sigset_t * set, int * sig) +{ +  volatile pthread_descr self = thread_self(); +  sigset_t mask; +  int s; +  sigjmp_buf jmpbuf; +  struct sigaction sa; + +  /* Get ready to block all signals except those in set +     and the cancellation signal. +     Also check that handlers are installed on all signals in set, +     and if not, install our dummy handler.  This is conformant to +     POSIX: "The effect of sigwait() on the signal actions for the +     signals in set is unspecified." */ +  sigfillset(&mask); +  sigdelset(&mask, __pthread_sig_cancel); +  for (s = 1; s <= NSIG; s++) { +    if (sigismember(set, s) && +        s != __pthread_sig_restart && +        s != __pthread_sig_cancel && +        s != __pthread_sig_debug) { +      sigdelset(&mask, s); +      if (sighandler[s].old == NULL || +	  sighandler[s].old == (arch_sighandler_t) SIG_DFL || +	  sighandler[s].old == (arch_sighandler_t) SIG_IGN) { +        sa.sa_handler = pthread_null_sighandler; +        sigemptyset(&sa.sa_mask); +        sa.sa_flags = 0; +        sigaction(s, &sa, NULL); +      } +    } +  } +  /* Test for cancellation */ +  if (sigsetjmp(jmpbuf, 1) == 0) { +    THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf); +    if (! (THREAD_GETMEM(self, p_canceled) +	   && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) { +      /* Reset the signal count */ +      THREAD_SETMEM(self, p_signal, 0); +      /* Say we're in sigwait */ +      THREAD_SETMEM(self, p_sigwaiting, 1); +      /* Unblock the signals and wait for them */ +      sigsuspend(&mask); +    } +  } +  THREAD_SETMEM(self, p_cancel_jmp, NULL); +  /* The signals are now reblocked.  Check for cancellation */ +  pthread_testcancel(); +  /* We should have self->p_signal != 0 and equal to the signal received */ +  *sig = THREAD_GETMEM(self, p_signal); +  return 0; +} + +/* Redefine raise() to send signal to calling thread only, +   as per POSIX 1003.1c */ +int raise (int sig) +{ +  int retcode = pthread_kill(pthread_self(), sig); +  if (retcode == 0) +    return 0; +  else { +    errno = retcode; +    return -1; +  } +} diff --git a/libpthread/linuxthreads/specific.c b/libpthread/linuxthreads/specific.c new file mode 100644 index 000000000..14c4b29d1 --- /dev/null +++ b/libpthread/linuxthreads/specific.c @@ -0,0 +1,179 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* mods for uClibc: removed strong_alias'es */ + +/* Thread-specific data */ + +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include "pthread.h" +#include "internals.h" + +/* Table of keys. */ + +static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = +  { { 0, NULL } }; + +/* Mutex to protect access to pthread_keys */ + +static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Create a new key */ + +int pthread_key_create(pthread_key_t * key, destr_function destr) +{ +  int i; + +  pthread_mutex_lock(&pthread_keys_mutex); +  for (i = 0; i < PTHREAD_KEYS_MAX; i++) { +    if (! pthread_keys[i].in_use) { +      /* Mark key in use */ +      pthread_keys[i].in_use = 1; +      pthread_keys[i].destr = destr; +      pthread_mutex_unlock(&pthread_keys_mutex); +      *key = i; +      return 0; +    } +  } +  pthread_mutex_unlock(&pthread_keys_mutex); +  return EAGAIN; +} +//strong_alias (__pthread_key_create, pthread_key_create) + +/* Delete a key */ + +int pthread_key_delete(pthread_key_t key) +{ +  pthread_descr self = thread_self(); +  pthread_descr th; +  unsigned int idx1st, idx2nd; + +  pthread_mutex_lock(&pthread_keys_mutex); +  if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) { +    pthread_mutex_unlock(&pthread_keys_mutex); +    return EINVAL; +  } +  pthread_keys[key].in_use = 0; +  pthread_keys[key].destr = NULL; +  /* Set the value of the key to NULL in all running threads, so +     that if the key is reallocated later by pthread_key_create, its +     associated values will be NULL in all threads. */ +  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE; +  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE; +  th = self; +  do { +    /* If the thread already is terminated don't modify the memory.  */ +    if (!th->p_terminated && th->p_specific[idx1st] != NULL) +      th->p_specific[idx1st][idx2nd] = NULL; +    th = th->p_nextlive; +  } while (th != self); +  pthread_mutex_unlock(&pthread_keys_mutex); +  return 0; +} + +/* Set the value of a key */ + +int pthread_setspecific(pthread_key_t key, const void * pointer) +{ +  pthread_descr self = thread_self(); +  unsigned int idx1st, idx2nd; + +  if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) +    return EINVAL; +  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE; +  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE; +  if (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL) { +    void *newp = calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *)); +    if (newp == NULL) +      return ENOMEM; +    THREAD_SETMEM_NC(self, p_specific[idx1st], newp); +  } +  THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd] = (void *) pointer; +  return 0; +} +//strong_alias (__pthread_setspecific, pthread_setspecific) + +/* Get the value of a key */ + +void * pthread_getspecific(pthread_key_t key) +{ +  pthread_descr self = thread_self(); +  unsigned int idx1st, idx2nd; + +  if (key >= PTHREAD_KEYS_MAX) +    return NULL; +  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE; +  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE; +  if (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL +      || !pthread_keys[key].in_use) +    return NULL; +  return THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd]; +} +//strong_alias (__pthread_getspecific, pthread_getspecific) + +/* Call the destruction routines on all keys */ + +void __pthread_destroy_specifics() +{ +  pthread_descr self = thread_self(); +  int i, j, round, found_nonzero; +  destr_function destr; +  void * data; + +  for (round = 0, found_nonzero = 1; +       found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS; +       round++) { +    found_nonzero = 0; +    for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) +      if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL) +        for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) { +          destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr; +          data = THREAD_GETMEM_NC(self, p_specific[i])[j]; +          if (destr != NULL && data != NULL) { +            THREAD_GETMEM_NC(self, p_specific[i])[j] = NULL; +            destr(data); +            found_nonzero = 1; +          } +        } +  } +  for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) { +    if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL) +      free(THREAD_GETMEM_NC(self, p_specific[i])); +  } +} + +/* Thread-specific data for libc. */ + +static int +libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer) +{ +  pthread_descr self = thread_self(); + +  THREAD_SETMEM_NC(self, p_libc_specific[key], (void *) pointer); +  return 0; +} +int (*__libc_internal_tsd_set)(enum __libc_tsd_key_t key, const void * pointer) +     = libc_internal_tsd_set; + +static void * +libc_internal_tsd_get(enum __libc_tsd_key_t key) +{ +  pthread_descr self = thread_self(); + +  return THREAD_GETMEM_NC(self, p_libc_specific[key]); +} +void * (*__libc_internal_tsd_get)(enum __libc_tsd_key_t key) +     = libc_internal_tsd_get; diff --git a/libpthread/linuxthreads/spinlock.c b/libpthread/linuxthreads/spinlock.c new file mode 100644 index 000000000..b1a99d975 --- /dev/null +++ b/libpthread/linuxthreads/spinlock.c @@ -0,0 +1,195 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +/* Internal locks */ + +#include <errno.h> +#include <sched.h> +#include <time.h> +#include "pthread.h" +#include "internals.h" +#include "spinlock.h" +#include "restart.h" + +/* The status field of a fastlock has the following meaning: +     0: fastlock is free +     1: fastlock is taken, no thread is waiting on it +  ADDR: fastlock is taken, ADDR is address of thread descriptor for +        first waiting thread, other waiting threads are linked via +        their p_nextlock field. +   The waiting list is not sorted by priority order. +   Actually, we always insert at top of list (sole insertion mode +   that can be performed without locking). +   For __pthread_unlock, we perform a linear search in the list +   to find the highest-priority, oldest waiting thread. +   This is safe because there are no concurrent __pthread_unlock +   operations -- only the thread that locked the mutex can unlock it. */ + +void internal_function __pthread_lock(struct _pthread_fastlock * lock, +				      pthread_descr self) +{ +  long oldstatus, newstatus; +  int spurious_wakeup_count = 0; + +  do { +    oldstatus = lock->__status; +    if (oldstatus == 0) { +      newstatus = 1; +    } else { +      if (self == NULL) +	self = thread_self(); +      newstatus = (long) self; +    } +    if (self != NULL) { +      ASSERT(self->p_nextlock == NULL); +      THREAD_SETMEM(self, p_nextlock, (pthread_descr) oldstatus); +    } +  } while(! compare_and_swap(&lock->__status, oldstatus, newstatus, +                             &lock->__spinlock)); + +  /* Suspend with guard against spurious wakeup.  +     This can happen in pthread_cond_timedwait_relative, when the thread +     wakes up due to timeout and is still on the condvar queue, and then +     locks the queue to remove itself. At that point it may still be on the +     queue, and may be resumed by a condition signal. */ + +  if (oldstatus != 0) { +    for (;;) { +      suspend(self); +      if (self->p_nextlock != NULL) { +	/* Count resumes that don't belong to us. */ +	spurious_wakeup_count++; +	continue; +      } +      break; +    } +  } + +  /* Put back any resumes we caught that don't belong to us. */ +  while (spurious_wakeup_count--) +    restart(self); +} + +void internal_function __pthread_unlock(struct _pthread_fastlock * lock) +{ +  long oldstatus; +  pthread_descr thr, * ptr, * maxptr; +  int maxprio; + +again: +  oldstatus = lock->__status; +  if (oldstatus == 0 || oldstatus == 1) { +    /* No threads are waiting for this lock.  Please note that we also +       enter this case if the lock is not taken at all.  If this wouldn't +       be done here we would crash further down.  */ +    if (! compare_and_swap(&lock->__status, oldstatus, 0, &lock->__spinlock)) +      goto again; +    return; +  } +  /* Find thread in waiting queue with maximal priority */ +  ptr = (pthread_descr *) &lock->__status; +  thr = (pthread_descr) oldstatus; +  maxprio = 0; +  maxptr = ptr; +  while (thr != (pthread_descr) 1) { +    if (thr->p_priority >= maxprio) { +      maxptr = ptr; +      maxprio = thr->p_priority; +    } +    ptr = &(thr->p_nextlock); +    thr = *ptr; +  } +  /* Remove max prio thread from waiting list. */ +  if (maxptr == (pthread_descr *) &lock->__status) { +    /* If max prio thread is at head, remove it with compare-and-swap +       to guard against concurrent lock operation */ +    thr = (pthread_descr) oldstatus; +    if (! compare_and_swap(&lock->__status, +                           oldstatus, (long)(thr->p_nextlock), +                           &lock->__spinlock)) +      goto again; +  } else { +    /* No risk of concurrent access, remove max prio thread normally */ +    thr = *maxptr; +    *maxptr = thr->p_nextlock; +  } +  /* Wake up the selected waiting thread */ +  thr->p_nextlock = NULL; +  restart(thr); +} + +/* Compare-and-swap emulation with a spinlock */ + +#ifdef TEST_FOR_COMPARE_AND_SWAP +int __pthread_has_cas = 0; +#endif + +#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP + +static void __pthread_acquire(int * spinlock); + +int __pthread_compare_and_swap(long * ptr, long oldval, long newval, +                               int * spinlock) +{ +  int res; +  if (testandset(spinlock)) __pthread_acquire(spinlock); +  if (*ptr == oldval) { +    *ptr = newval; res = 1; +  } else { +    res = 0; +  } +  *spinlock = 0; +  return res; +} + +/* This function is called if the inlined test-and-set +   in __pthread_compare_and_swap() failed */ + +/* The retry strategy is as follows: +   - We test and set the spinlock MAX_SPIN_COUNT times, calling +     sched_yield() each time.  This gives ample opportunity for other +     threads with priority >= our priority to make progress and +     release the spinlock. +   - If a thread with priority < our priority owns the spinlock, +     calling sched_yield() repeatedly is useless, since we're preventing +     the owning thread from making progress and releasing the spinlock. +     So, after MAX_SPIN_LOCK attemps, we suspend the calling thread +     using nanosleep().  This again should give time to the owning thread +     for releasing the spinlock. +     Notice that the nanosleep() interval must not be too small, +     since the kernel does busy-waiting for short intervals in a realtime +     process (!).  The smallest duration that guarantees thread +     suspension is currently 2ms. +   - When nanosleep() returns, we try again, doing MAX_SPIN_COUNT +     sched_yield(), then sleeping again if needed. */ + +static void __pthread_acquire(int * spinlock) +{ +  int cnt = 0; +  struct timespec tm; + +  while (testandset(spinlock)) { +    if (cnt < MAX_SPIN_COUNT) { +      sched_yield(); +      cnt++; +    } else { +      tm.tv_sec = 0; +      tm.tv_nsec = SPIN_SLEEP_DURATION; +      nanosleep(&tm, NULL); +      cnt = 0; +    } +  } +} + +#endif diff --git a/libpthread/linuxthreads/spinlock.h b/libpthread/linuxthreads/spinlock.h new file mode 100644 index 000000000..aae18a27b --- /dev/null +++ b/libpthread/linuxthreads/spinlock.h @@ -0,0 +1,102 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +#if defined(TEST_FOR_COMPARE_AND_SWAP) + +extern int __pthread_has_cas; +extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, +                                      int * spinlock); + +static inline int compare_and_swap(long * ptr, long oldval, long newval, +                                   int * spinlock) +{ +  if (__builtin_expect (__pthread_has_cas, 1)) +    return __compare_and_swap(ptr, oldval, newval); +  else +    return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); +} + +#elif defined(HAS_COMPARE_AND_SWAP) + +static inline int compare_and_swap(long * ptr, long oldval, long newval, +                                   int * spinlock) +{ +  return __compare_and_swap(ptr, oldval, newval); +} + +#else + +extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, +                                      int * spinlock); + +static inline int compare_and_swap(long * ptr, long oldval, long newval, +                                   int * spinlock) +{ +  return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); +} + +#endif + +/* Internal locks */ + +extern void internal_function __pthread_lock(struct _pthread_fastlock * lock, +					     pthread_descr self); +extern void internal_function __pthread_unlock(struct _pthread_fastlock *lock); + +static inline void __pthread_init_lock(struct _pthread_fastlock * lock) +{ +  lock->__status = 0; +  lock->__spinlock = 0; +} + +static inline int __pthread_trylock (struct _pthread_fastlock * lock) +{ +  long oldstatus; + +  do { +    oldstatus = lock->__status; +    if (oldstatus != 0) return EBUSY; +  } while(! compare_and_swap(&lock->__status, 0, 1, &lock->__spinlock)); +  return 0; +} + +#define LOCK_INITIALIZER {0, 0} + +/* Operations on pthread_atomic, which is defined in internals.h */ + +static inline long atomic_increment(struct pthread_atomic *pa) +{ +    long oldval; + +    do { +	oldval = pa->p_count; +    } while (!compare_and_swap(&pa->p_count, oldval, oldval + 1, &pa->p_spinlock)); + +    return oldval; +} + + +static inline long atomic_decrement(struct pthread_atomic *pa) +{ +    long oldval; + +    do { +	oldval = pa->p_count; +    } while (!compare_and_swap(&pa->p_count, oldval, oldval - 1, &pa->p_spinlock)); + +    return oldval; +} + +#define ATOMIC_INITIALIZER { 0, 0 } + diff --git a/libpthread/linuxthreads/sysdeps/alpha/pt-machine.h b/libpthread/linuxthreads/sysdeps/alpha/pt-machine.h new file mode 100644 index 000000000..e59c6906c --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/alpha/pt-machine.h @@ -0,0 +1,108 @@ +/* Machine-dependent pthreads configuration and inline functions. +   Alpha version. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc.,  59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + +#include <asm/pal.h> + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char *stack_pointer __asm__("$30"); + + +/* Spinlock implementation; required.  */ +PT_EI long int +testandset (int *spinlock) +{ +  long int ret, temp; + +  __asm__ __volatile__( +	"/* Inline spinlock test & set */\n" +	"1:\t" +	"ldl_l %0,%3\n\t" +	"bne %0,2f\n\t" +	"or $31,1,%1\n\t" +	"stl_c %1,%2\n\t" +	"beq %1,1b\n" +	"2:\tmb\n" +	"/* End spinlock test & set */" +	: "=&r"(ret), "=&r"(temp), "=m"(*spinlock) +	: "m"(*spinlock) +        : "memory"); + +  return ret; +} + +/* Spinlock release; default is just set to zero.  */ +#define RELEASE(spinlock) \ +  __asm__ __volatile__("mb" : : : "memory"); \ +  *spinlock = 0 + + +/* Begin allocating thread stacks at this address.  Default is to allocate +   them just below the initial program stack.  */ +#define THREAD_STACK_START_ADDRESS  0x40000000000 + + +/* Return the thread descriptor for the current thread.  */ +#define THREAD_SELF \ +({									      \ +  register pthread_descr __self __asm__("$0");				      \ +  __asm__ ("call_pal %1" : "=r"(__self) : "i"(PAL_rduniq) : "$0");	      \ +  __self;								      \ +}) + +/* Initialize the thread-unique value.  */ +#define INIT_THREAD_SELF(descr, nr) \ +{									      \ +  register pthread_descr __self __asm__("$16") = (descr);		      \ +  __asm__ __volatile__ ("call_pal %1" : : "r"(__self), "i"(PAL_wruniq));      \ +} + + +/* Compare-and-swap for semaphores. */ + +#define HAS_COMPARE_AND_SWAP +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  long int ret; + +  __asm__ __volatile__ ( +	"/* Inline compare & swap */\n" +	"1:\t" +	"ldq_l %0,%4\n\t" +	"cmpeq %0,%2,%0\n\t" +	"beq %0,2f\n\t" +	"mov %3,%0\n\t" +	"stq_c %0,%1\n\t" +	"beq %0,1b\n\t" +	"2:\tmb\n" +	"/* End compare & swap */" +	: "=&r"(ret), "=m"(*p) +	: "r"(oldval), "r"(newval), "m"(*p)); + +  return ret; +} diff --git a/libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h new file mode 100644 index 000000000..eb6f4f075 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h @@ -0,0 +1,25 @@ +/* Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#define SIGCONTEXT struct sigcontext +#define SIGCONTEXT_EXTRA_ARGS +#define GET_PC(ctx)	((void *) (ctx).sc_pc) +#define GET_FRAME(ctx)	((void *) (ctx).sc_regs[15]) +#define GET_STACK(ctx)	((void *) (ctx).sc_regs[30]) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h b/libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h new file mode 100644 index 000000000..4530cdbda --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h @@ -0,0 +1,73 @@ +/* Definition of `struct sigcontext' for Linux/ARM +   Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +/* The format of struct sigcontext changed between 2.0 and 2.1 kernels. +   Fortunately 2.0 puts a magic number in the first word and this is not +   a legal value for `trap_no', so we can tell them apart.  */ + +/* Early 2.2 and 2.3 kernels do not have the `fault_address' member in +   the sigcontext structure.  Unfortunately there is no reliable way +   to test for its presence and this word will contain garbage for too-old +   kernels.  Versions 2.2.14 and 2.3.35 (plus later versions) are known to +   include this element.  */ + +#ifndef __ARMSIGCTX_H +#define __ARMSIGCTX_H	1 + +#include <asm/ptrace.h> + +union k_sigcontext +  { +    struct +      { +	unsigned long int trap_no; +	unsigned long int error_code; +	unsigned long int oldmask; +	unsigned long int arm_r0; +	unsigned long int arm_r1; +	unsigned long int arm_r2; +	unsigned long int arm_r3; +	unsigned long int arm_r4; +	unsigned long int arm_r5; +	unsigned long int arm_r6; +	unsigned long int arm_r7; +	unsigned long int arm_r8; +	unsigned long int arm_r9; +	unsigned long int arm_r10; +	unsigned long int arm_fp; +	unsigned long int arm_ip; +	unsigned long int arm_sp; +	unsigned long int arm_lr; +	unsigned long int arm_pc; +	unsigned long int arm_cpsr; +	unsigned long fault_address; +      } v21; +    struct +      { +	unsigned long int magic; +	struct pt_regs reg; +	unsigned long int trap_no; +	unsigned long int error_code; +	unsigned long int oldmask; +      } v20; +}; + +#define SIGCONTEXT_2_0_MAGIC	0x4B534154 + +#endif	/* bits/armsigctx.h */ diff --git a/libpthread/linuxthreads/sysdeps/arm/pt-machine.h b/libpthread/linuxthreads/sysdeps/arm/pt-machine.h new file mode 100644 index 000000000..d4dc4c4ed --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/arm/pt-machine.h @@ -0,0 +1,48 @@ +/* Machine-dependent pthreads configuration and inline functions. +   ARM version. +   Copyright (C) 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Philip Blundell <philb@gnu.org>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + + +/* This will not work on ARM1 or ARM2 because SWP is lacking on those +   machines.  Unfortunately we have no way to detect this at compile +   time; let's hope nobody tries to use one.  */ + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  register unsigned int ret; + +  __asm__ __volatile__("swp %0, %1, [%2]" +		       : "=r"(ret) +		       : "0"(1), "r"(spinlock)); + +  return ret; +} + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("sp"); diff --git a/libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h b/libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h new file mode 100644 index 000000000..8d9ea709b --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h @@ -0,0 +1,67 @@ +/* Machine-dependent pthreads configuration and inline functions. +   i686 version. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("%esp"); + + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  int ret; + +  __asm__ __volatile__ ( +	"xchgl %0, %1" +	: "=r"(ret), "=m"(*spinlock) +	: "0"(1), "m"(*spinlock) +	: "memory"); + +  return ret; +} + + +/* Compare-and-swap for semaphores.  It's always available on i686.  */ +#define HAS_COMPARE_AND_SWAP + +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  char ret; +  long int readval; + +  __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0" +			: "=q" (ret), "=m" (*p), "=a" (readval) +			: "r" (newval), "m" (*p), "a" (oldval) +			: "memory"); +  return ret; +} + + +/* Use the LDT implementation only if the kernel is fixed.  */ +//#include "../useldt.h" diff --git a/libpthread/linuxthreads/sysdeps/i386/pt-machine.h b/libpthread/linuxthreads/sysdeps/i386/pt-machine.h new file mode 100644 index 000000000..f542bb2d2 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/i386/pt-machine.h @@ -0,0 +1,99 @@ +/* Machine-dependent pthreads configuration and inline functions. +   i386 version. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("%esp"); + + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  int ret; + +  __asm__ __volatile__( +       "xchgl %0, %1" +       : "=r"(ret), "=m"(*spinlock) +       : "0"(1), "m"(*spinlock) +       : "memory"); + +  return ret; +} + + +/* Compare-and-swap for semaphores. +   Available on the 486 and above, but not on the 386. +   We test dynamically whether it's available or not. */ + +#define HAS_COMPARE_AND_SWAP +#define TEST_FOR_COMPARE_AND_SWAP + +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  char ret; +  long int readval; + +  __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0" +			: "=q" (ret), "=m" (*p), "=a" (readval) +			: "r" (newval), "m" (*p), "a" (oldval) +			: "memory"); +  return ret; +} + + +PT_EI int +get_eflags (void) +{ +  int res; +  __asm__ __volatile__ ("pushfl; popl %0" : "=r" (res) : ); +  return res; +} + + +PT_EI void +set_eflags (int newflags) +{ +  __asm__ __volatile__ ("pushl %0; popfl" : : "r" (newflags) : "cc"); +} + + +PT_EI int +compare_and_swap_is_available (void) +{ +  int oldflags = get_eflags (); +  int changed; +  /* Flip AC bit in EFLAGS.  */ +  set_eflags (oldflags ^ 0x40000); +  /* See if bit changed.  */ +  changed = (get_eflags () ^ oldflags) & 0x40000; +  /* Restore EFLAGS.  */ +  set_eflags (oldflags); +  /* If the AC flag did not change, it's a 386 and it lacks cmpxchg. +     Otherwise, it's a 486 or above and it has cmpxchg.  */ +  return changed != 0; +} diff --git a/libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h new file mode 100644 index 000000000..42c18b22b --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h @@ -0,0 +1,24 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#define SIGCONTEXT struct sigcontext +#define SIGCONTEXT_EXTRA_ARGS +#define GET_PC(ctx)	((void *) ctx.eip) +#define GET_FRAME(ctx)	((void *) ctx.ebp) +#define GET_STACK(ctx)	((void *) ctx.esp_at_signal) diff --git a/libpthread/linuxthreads/sysdeps/i386/useldt.h b/libpthread/linuxthreads/sysdeps/i386/useldt.h new file mode 100644 index 000000000..1a789e2e0 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/i386/useldt.h @@ -0,0 +1,170 @@ +/* Special definitions for ix86 machine using segment register based +   thread descriptor. +   Copyright (C) 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h>	/* For offsetof.  */ + + +/* We don't want to include the kernel header.  So duplicate the +   information.  */ + +/* Structure passed on `modify_ldt' call.  */ +struct modify_ldt_ldt_s +{ +  unsigned int entry_number; +  unsigned long int base_addr; +  unsigned int limit; +  unsigned int seg_32bit:1; +  unsigned int contents:2; +  unsigned int read_exec_only:1; +  unsigned int limit_in_pages:1; +  unsigned int seg_not_present:1; +  unsigned int useable:1; +  unsigned int empty:25; +}; + +/* System call to set LDT entry.  */ +extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); + + +/* Return the thread descriptor for the current thread. + +   The contained asm must *not* be marked volatile since otherwise +   assignments like +	pthread_descr self = thread_self(); +   do not get optimized away.  */ +#define THREAD_SELF \ +({									      \ +  register pthread_descr __self;					      \ +  __asm__ ("movl %%gs:%c1,%0" : "=r" (__self)				      \ +	   : "i" (offsetof (struct _pthread_descr_struct, p_self)));	      \ +  __self;								      \ +}) + +/* Initialize the thread-unique value.  */ +#define INIT_THREAD_SELF(descr, nr) \ +{									      \ +  struct modify_ldt_ldt_s ldt_entry =					      \ +    { nr, (unsigned long int) descr, sizeof (*descr), 1, 0, 0, 0, 0, 1, 0 };  \ +  if (__modify_ldt (1, &ldt_entry, sizeof (ldt_entry)) != 0)		      \ +    abort ();								      \ +  __asm__ __volatile__ ("movw %w0, %%gs" : : "r" (nr * 8 + 7));		      \ +} + +/* Free resources associated with thread descriptor.  */ +#define FREE_THREAD_SELF(descr, nr) \ +{									      \ +  struct modify_ldt_ldt_s ldt_entry =					      \ +    { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };					      \ +  __asm__ __volatile__ ("movw %w0,%%gs" : : "r" (0));			      \ +  __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));			      \ +} + +/* Read member of the thread descriptor directly.  */ +#define THREAD_GETMEM(descr, member) \ +({									      \ +  __typeof__ (descr->member) __value;					      \ +  if (sizeof (__value) == 1)						      \ +    __asm__ __volatile__ ("movb %%gs:%P2,%b0"				      \ +			  : "=r" (__value)				      \ +			  : "0" (0),					      \ +			    "i" (offsetof (struct _pthread_descr_struct,      \ +					   member)));			      \ +  else									      \ +    {									      \ +      if (sizeof (__value) != 4)					      \ +	/* There should not be any value with a size other than 1 or 4.  */   \ +	abort ();							      \ +									      \ +      __asm__ __volatile__ ("movl %%gs:%P1,%0"				      \ +			    : "=r" (__value)				      \ +			    : "i" (offsetof (struct _pthread_descr_struct,    \ +					     member)));			      \ +    }									      \ +  __value;								      \ +}) + +/* Same as THREAD_GETMEM, but the member offset can be non-constant.  */ +#define THREAD_GETMEM_NC(descr, member) \ +({									      \ +  __typeof__ (descr->member) __value;					      \ +  if (sizeof (__value) == 1)						      \ +    __asm__ __volatile__ ("movb %%gs:(%2),%b0"				      \ +			  : "=r" (__value)				      \ +			  : "0" (0),					      \ +			    "r" (offsetof (struct _pthread_descr_struct,      \ +					   member)));			      \ +  else									      \ +    {									      \ +      if (sizeof (__value) != 4)					      \ +	/* There should not be any value with a size other than 1 or 4.  */   \ +	abort ();							      \ +									      \ +      __asm__ __volatile__ ("movl %%gs:(%1),%0"				      \ +			    : "=r" (__value)				      \ +			    : "r" (offsetof (struct _pthread_descr_struct,    \ +					     member)));			      \ +    }									      \ +  __value;								      \ +}) + +/* Same as THREAD_SETMEM, but the member offset can be non-constant.  */ +#define THREAD_SETMEM(descr, member, value) \ +({									      \ +  __typeof__ (descr->member) __value = (value);				      \ +  if (sizeof (__value) == 1)						      \ +    __asm__ __volatile__ ("movb %0,%%gs:%P1" :				      \ +			  : "r" (__value),				      \ +			    "i" (offsetof (struct _pthread_descr_struct,      \ +					   member)));			      \ +  else									      \ +    {									      \ +      if (sizeof (__value) != 4)					      \ +	/* There should not be any value with a size other than 1 or 4.  */   \ +	abort ();							      \ +									      \ +      __asm__ __volatile__ ("movl %0,%%gs:%P1" :			      \ +			    : "r" (__value),				      \ +			      "i" (offsetof (struct _pthread_descr_struct,    \ +					     member)));			      \ +    }									      \ +}) + +/* Set member of the thread descriptor directly.  */ +#define THREAD_SETMEM_NC(descr, member, value) \ +({									      \ +  __typeof__ (descr->member) __value = (value);				      \ +  if (sizeof (__value) == 1)						      \ +    __asm__ __volatile__ ("movb %0,%%gs:(%1)" :				      \ +			  : "r" (__value),				      \ +			    "r" (offsetof (struct _pthread_descr_struct,      \ +					   member)));			      \ +  else									      \ +    {									      \ +      if (sizeof (__value) != 4)					      \ +	/* There should not be any value with a size other than 1 or 4.  */   \ +	abort ();							      \ +									      \ +      __asm__ __volatile__ ("movl %0,%%gs:(%1)" :			      \ +			    : "r" (__value),				      \ +			      "r" (offsetof (struct _pthread_descr_struct,    \ +					     member)));			      \ +    }									      \ +}) diff --git a/libpthread/linuxthreads/sysdeps/m68k/pt-machine.h b/libpthread/linuxthreads/sysdeps/m68k/pt-machine.h new file mode 100644 index 000000000..38ea68114 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/m68k/pt-machine.h @@ -0,0 +1,62 @@ +/* Machine-dependent pthreads configuration and inline functions. +   m68k version. +   Copyright (C) 1996, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If +   not, write to the Free Software Foundation, Inc., +   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  char ret; + +  __asm__ __volatile__("tas %1; sne %0" +       : "=dm"(ret), "=m"(*spinlock) +       : "m"(*spinlock) +       : "cc"); + +  return ret; +} + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("%sp"); + + +/* Compare-and-swap for semaphores. */ + +#define HAS_COMPARE_AND_SWAP +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  char ret; +  long int readval; + +  __asm__ __volatile__ ("casl %2, %3, %1; seq %0" +			: "=dm" (ret), "=m" (*p), "=d" (readval) +			: "d" (newval), "m" (*p), "2" (oldval)); + +  return ret; +} diff --git a/libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h new file mode 100644 index 000000000..b7e08cfc9 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h @@ -0,0 +1,26 @@ +/* Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>, 1998. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#define SIGCONTEXT int _code, struct sigcontext * +#define SIGCONTEXT_EXTRA_ARGS _code, +#define GET_PC(ctx)	((void *) (ctx)->sc_pc) +#define GET_FRAME(ctx)	((void *) __builtin_frame_address (1)) +#define GET_STACK(ctx)	((void *) (ctx)->sc_usp) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/mips/pt-machine.h b/libpthread/linuxthreads/sysdeps/mips/pt-machine.h new file mode 100644 index 000000000..527392308 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/mips/pt-machine.h @@ -0,0 +1,90 @@ +/* Machine-dependent pthreads configuration and inline functions. + +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>. +   Based on the Alpha version by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If +   not, write to the Free Software Foundation, Inc., +   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +   TODO: This version makes use of MIPS ISA 2 features.  It won't +   work on ISA 1.  These machines will have to take the overhead of +   a sysmips(MIPS_ATOMIC_SET, ...) syscall which isn't implemented +   yet correctly.  There is however a better solution for R3000 +   uniprocessor machines possible.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + + +/* Spinlock implementation; required.  */ +PT_EI long int +testandset (int *spinlock) +{ +  long int ret, temp; + +  __asm__ __volatile__( +	"# Inline spinlock test & set\n\t" +	".set\tmips2\n" +	"1:\tll\t%0,%3\n\t" +	"bnez\t%0,2f\n\t" +	".set\tnoreorder\n\t" +	"li\t%1,1\n\t" +	".set\treorder\n\t" +	"sc\t%1,%2\n\t" +	"beqz\t%1,1b\n" +	"2:\t.set\tmips0\n\t" +	"/* End spinlock test & set */" +	: "=&r"(ret), "=&r" (temp), "=m"(*spinlock) +	: "m"(*spinlock) +	: "memory"); + +  return ret; +} + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("$29"); + + +/* Compare-and-swap for semaphores. */ + +#define HAS_COMPARE_AND_SWAP +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  long ret; + +  __asm__ __volatile__ ( +	"/* Inline compare & swap */\n\t" +	".set\tmips2\n" +	"1:\tll\t%0,%4\n\t" +	".set\tnoreorder\n\t" +	"bne\t%0,%2,2f\n\t" +	"move\t%0,%3\n\t" +	".set\treorder\n\t" +	"sc\t%0,%1\n\t" +	"beqz\t%0,1b\n" +	"2:\t.set\tmips0\n\t" +	"/* End compare & swap */" +	: "=&r"(ret), "=m"(*p) +	: "r"(oldval), "r"(newval), "m"(*p)); + +  return ret; +} diff --git a/libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h new file mode 100644 index 000000000..a51c6f043 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Andreas Jaeger <aj@suse.de>, 2000. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + + +#define SIGCONTEXT unsigned long _code, struct sigcontext * +#define SIGCONTEXT_EXTRA_ARGS _code, +#define GET_PC(ctx)	((void *) ctx->sc_pc) +#define GET_FRAME(ctx)	((void *) ctx->sc_regs[30]) +#define GET_STACK(ctx)	((void *) ctx->sc_regs[29]) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h b/libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h new file mode 100644 index 000000000..578369a7f --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h @@ -0,0 +1,69 @@ +/* Machine-dependent pthreads configuration and inline functions. +   powerpc version. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If +   not, write to the Free Software Foundation, Inc., +   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ + +/* These routines are from Appendix G of the 'PowerPC 601 RISC Microprocessor +   User's Manual', by IBM and Motorola.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + +/* For multiprocessor systems, we want to ensure all memory accesses +   are completed before we reset a lock.  */ +#if 0 +/* on non multiprocessor systems, you can just: */ +#define sync() /* nothing */ +#else +#define sync() __asm__ __volatile__ ("sync") +#endif + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__ ("r1"); + +/* Compare-and-swap for semaphores. */ +/* note that test-and-set(x) is the same as compare-and-swap(x, 0, 1) */ + +#define HAS_COMPARE_AND_SWAP +#if BROKEN_PPC_ASM_CR0 +static +#else +PT_EI +#endif +int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  int ret; + +  sync(); +  __asm__ __volatile__( +		       "0:    lwarx %0,0,%1 ;" +		       "      xor. %0,%3,%0;" +		       "      bne 1f;" +		       "      stwcx. %2,0,%1;" +		       "      bne- 0b;" +		       "1:    " +	: "=&r"(ret) +	: "r"(p), "r"(newval), "r"(oldval) +	: "cr0", "memory"); +  sync(); +  return ret == 0; +} diff --git a/libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h new file mode 100644 index 000000000..138a15cfa --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h @@ -0,0 +1,27 @@ +/* Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <signal.h> + +#define SIGCONTEXT struct sigcontext * +#define SIGCONTEXT_EXTRA_ARGS +#define GET_PC(ctx)	((void *)((ctx)->regs->nip)) +#define GET_FRAME(ctx)	(*(void **)((ctx)->regs->gpr[1])) +#define GET_STACK(ctx)	((void *)((ctx)->regs->gpr[1])) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h new file mode 100644 index 000000000..a14cea1aa --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h @@ -0,0 +1,214 @@ +/* libc-internal interface for mutex locks.  LinuxThreads version. +   Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef _BITS_LIBC_LOCK_H +#define _BITS_LIBC_LOCK_H 1 + +#include <pthread.h> + +/* Mutex type.  */ +#ifdef _LIBC +typedef pthread_mutex_t __libc_lock_t; +#else +typedef struct __libc_lock_opaque__ __libc_lock_t; +#endif + +/* Type for key to thread-specific data.  */ +typedef pthread_key_t __libc_key_t; + +/* Define a lock variable NAME with storage class CLASS.  The lock must be +   initialized with __libc_lock_init before it can be used (or define it +   with __libc_lock_define_initialized, below).  Use `extern' for CLASS to +   declare a lock defined in another module.  In public structure +   definitions you must use a pointer to the lock structure (i.e., NAME +   begins with a `*'), because its storage size will not be known outside +   of libc.  */ +#define __libc_lock_define(CLASS,NAME) \ +  CLASS __libc_lock_t NAME; + +/* Define an initialized lock variable NAME with storage class CLASS. + +   For the C library we take a deeper look at the initializer.  For this +   implementation all fields are initialized to zero.  Therefore we +   don't initialize the variable which allows putting it into the BSS +   section.  */ +#define __libc_lock_define_initialized(CLASS,NAME) \ +  CLASS __libc_lock_t NAME; + +/* Define an initialized recursive lock variable NAME with storage +   class CLASS.  */ +#define __libc_lock_define_initialized_recursive(CLASS,NAME) \ +  CLASS __libc_lock_t NAME = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +/* Initialize the named lock variable, leaving it in a consistent, unlocked +   state.  */ +#define __libc_lock_init(NAME) \ +  (__pthread_mutex_init != NULL ? __pthread_mutex_init (&(NAME), NULL) : 0); + +/* Same as last but this time we initialize a recursive mutex.  */ +#define __libc_lock_init_recursive(NAME) \ +  do {									      \ +    if (__pthread_mutex_init != NULL)					      \ +      {									      \ +	pthread_mutexattr_t __attr;					      \ +	__pthread_mutexattr_init (&__attr);				      \ +	__pthread_mutexattr_settype (&__attr, PTHREAD_MUTEX_RECURSIVE_NP); \ +	__pthread_mutex_init (&(NAME), &__attr);			      \ +	__pthread_mutexattr_destroy (&__attr);				      \ +      }									      \ +  } while (0); + +/* Finalize the named lock variable, which must be locked.  It cannot be +   used again until __libc_lock_init is called again on it.  This must be +   called on a lock variable before the containing storage is reused.  */ +#define __libc_lock_fini(NAME) \ +  (__pthread_mutex_destroy != NULL ? __pthread_mutex_destroy (&(NAME)) : 0); + +/* Finalize recursive named lock.  */ +#define __libc_lock_fini_recursive(NAME) __libc_lock_fini (NAME) + +/* Lock the named lock variable.  */ +#define __libc_lock_lock(NAME) \ +  (__pthread_mutex_lock != NULL ? __pthread_mutex_lock (&(NAME)) : 0); + +/* Lock the recursive named lock variable.  */ +#define __libc_lock_lock_recursive(NAME) __libc_lock_lock (NAME) + +/* Try to lock the named lock variable.  */ +#define __libc_lock_trylock(NAME) \ +  (__pthread_mutex_trylock != NULL ? __pthread_mutex_trylock (&(NAME)) : 0) + +/* Try to lock the recursive named lock variable.  */ +#define __libc_lock_trylock_recursive(NAME) __libc_lock_trylock (NAME) + +/* Unlock the named lock variable.  */ +#define __libc_lock_unlock(NAME) \ +  (__pthread_mutex_unlock != NULL ? __pthread_mutex_unlock (&(NAME)) : 0); + +/* Unlock the recursive named lock variable.  */ +#define __libc_lock_unlock_recursive(NAME) __libc_lock_unlock (NAME) + + +/* Define once control variable.  */ +#if PTHREAD_ONCE_INIT == 0 +/* Special case for static variables where we can avoid the initialization +   if it is zero.  */ +# define __libc_once_define(CLASS, NAME) \ +  CLASS pthread_once_t NAME +#else +# define __libc_once_define(CLASS, NAME) \ +  CLASS pthread_once_t NAME = PTHREAD_ONCE_INIT +#endif + +/* Call handler iff the first call.  */ +#define __libc_once(ONCE_CONTROL, INIT_FUNCTION) \ +  do {									      \ +    if (__pthread_once != NULL)						      \ +      __pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION));		      \ +    else if ((ONCE_CONTROL) == 0) {					      \ +      INIT_FUNCTION ();							      \ +      (ONCE_CONTROL) = 1;						      \ +    }									      \ +  } while (0) + + +/* Start critical region with cleanup.  */ +#define __libc_cleanup_region_start(FCT, ARG) \ +  { struct _pthread_cleanup_buffer _buffer;				      \ +    int _avail = _pthread_cleanup_push_defer != NULL;			      \ +    if (_avail) {							      \ +      _pthread_cleanup_push_defer (&_buffer, (FCT), (ARG));		      \ +    } + +/* End critical region with cleanup.  */ +#define __libc_cleanup_region_end(DOIT) \ +    if (_avail) {							      \ +      _pthread_cleanup_pop_restore (&_buffer, (DOIT));			      \ +    }									      \ +  } + +/* Sometimes we have to exit the block in the middle.  */ +#define __libc_cleanup_end(DOIT) \ +    if (_avail) {							      \ +      _pthread_cleanup_pop_restore (&_buffer, (DOIT));			      \ +    } + +/* Create thread-specific key.  */ +#define __libc_key_create(KEY, DESTRUCTOR) \ +  (__pthread_key_create != NULL ? __pthread_key_create (KEY, DESTRUCTOR) : 1) + +/* Get thread-specific data.  */ +#define __libc_getspecific(KEY) \ +  (__pthread_getspecific != NULL ? __pthread_getspecific (KEY) : NULL) + +/* Set thread-specific data.  */ +#define __libc_setspecific(KEY, VALUE) \ +  (__pthread_setspecific != NULL ? __pthread_setspecific (KEY, VALUE) : 0) + + +/* Register handlers to execute before and after `fork'.  */ +#define __libc_atfork(PREPARE, PARENT, CHILD) \ +  (__pthread_atfork != NULL ? __pthread_atfork (PREPARE, PARENT, CHILD) : 0) + + +/* Make the pthread functions weak so that we can elide them from +   single-threaded processes.  */ +#ifndef __NO_WEAK_PTHREAD_ALIASES +# ifdef weak_extern +weak_extern (__pthread_mutex_init) +weak_extern (__pthread_mutex_destroy) +weak_extern (__pthread_mutex_lock) +weak_extern (__pthread_mutex_trylock) +weak_extern (__pthread_mutex_unlock) +weak_extern (__pthread_mutexattr_init) +weak_extern (__pthread_mutexattr_destroy) +weak_extern (__pthread_mutexattr_settype) +weak_extern (__pthread_key_create) +weak_extern (__pthread_setspecific) +weak_extern (__pthread_getspecific) +weak_extern (__pthread_once) +weak_extern (__pthread_initialize) +weak_extern (__pthread_atfork) +weak_extern (_pthread_cleanup_push_defer) +weak_extern (_pthread_cleanup_pop_restore) +# else +#  pragma weak __pthread_mutex_init +#  pragma weak __pthread_mutex_destroy +#  pragma weak __pthread_mutex_lock +#  pragma weak __pthread_mutex_trylock +#  pragma weak __pthread_mutex_unlock +#  pragma weak __pthread_mutexattr_init +#  pragma weak __pthread_mutexattr_destroy +#  pragma weak __pthread_mutexattr_settype +#  pragma weak __pthread_key_create +#  pragma weak __pthread_setspecific +#  pragma weak __pthread_getspecific +#  pragma weak __pthread_once +#  pragma weak __pthread_initialize +#  pragma weak __pthread_atfork +#  pragma weak _pthread_cleanup_push_defer +#  pragma weak _pthread_cleanup_pop_restore +# endif +#endif + +/* We need portable names for some functions.  E.g., when they are +   used as argument to __libc_cleanup_region_start.  */ +#define __libc_mutex_unlock __pthread_mutex_unlock + +#endif	/* bits/libc-lock.h */ diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h new file mode 100644 index 000000000..e38cdf550 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h @@ -0,0 +1,44 @@ +/* libc-internal interface for thread-specific data.  LinuxThreads version. +   Copyright (C) 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef _BITS_LIBC_TSD_H +#define _BITS_LIBC_TSD_H 1 + +#include <features.h> + +/* Fast thread-specific data internal to libc.  */ +enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, +			_LIBC_TSD_KEY_DL_ERROR, +			_LIBC_TSD_KEY_N }; + +extern void *(*__libc_internal_tsd_get) __P ((enum __libc_tsd_key_t)); +extern int (*__libc_internal_tsd_set) __P ((enum __libc_tsd_key_t, +					    __const void *)); + +#define __libc_tsd_define(CLASS, KEY)	CLASS void *__libc_tsd_##KEY##_data; +#define __libc_tsd_get(KEY) \ +  (__libc_internal_tsd_get != NULL \ +   ? __libc_internal_tsd_get (_LIBC_TSD_KEY_##KEY) \ +   : __libc_tsd_##KEY##_data) +#define __libc_tsd_set(KEY, VALUE) \ +  (__libc_internal_tsd_set != NULL \ +   ? __libc_internal_tsd_set (_LIBC_TSD_KEY_##KEY, (VALUE)) \ +   : ((__libc_tsd_##KEY##_data = (VALUE)), 0)) + +#endif	/* bits/libc-tsd.h */ diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h b/libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h new file mode 100644 index 000000000..db4c3790c --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h @@ -0,0 +1,122 @@ +/* Linuxthreads - a simple clone()-based implementation of Posix        */ +/* threads for Linux.                                                   */ +/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */ +/*                                                                      */ +/* This program is free software; you can redistribute it and/or        */ +/* modify it under the terms of the GNU Library 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 Library General Public License for more details.                 */ + +#if !defined _BITS_TYPES_H && !defined _PTHREAD_H +# error "Never include <bits/pthreadtypes.h> directly; use <sys/types.h> instead." +#endif + +#ifndef _BITS_PTHREADTYPES_H +#define _BITS_PTHREADTYPES_H	1 + +#define __need_schedparam +#include <bits/sched.h> + +/* Fast locks (not abstract because mutexes and conditions aren't abstract). */ +struct _pthread_fastlock +{ +  long int __status;            /* "Free" or "taken" or head of waiting list */ +  int __spinlock;               /* For compare-and-swap emulation */ +}; + +#ifndef _PTHREAD_DESCR_DEFINED +/* Thread descriptors */ +typedef struct _pthread_descr_struct *_pthread_descr; +# define _PTHREAD_DESCR_DEFINED +#endif + + +/* Attributes for threads.  */ +typedef struct +{ +  int __detachstate; +  int __schedpolicy; +  struct __sched_param __schedparam; +  int __inheritsched; +  int __scope; +  size_t __guardsize; +  int __stackaddr_set; +  void *__stackaddr; +  size_t __stacksize; +} pthread_attr_t; + + +/* Conditions (not abstract because of PTHREAD_COND_INITIALIZER */ +typedef struct +{ +  struct _pthread_fastlock __c_lock; /* Protect against concurrent access */ +  _pthread_descr __c_waiting;        /* Threads waiting on this condition */ +} pthread_cond_t; + + +/* Attribute for conditionally variables.  */ +typedef struct +{ +  int __dummy; +} pthread_condattr_t; + +/* Keys for thread-specific data */ +typedef unsigned int pthread_key_t; + + +/* Mutexes (not abstract because of PTHREAD_MUTEX_INITIALIZER).  */ +/* (The layout is unnatural to maintain binary compatibility +    with earlier releases of LinuxThreads.) */ +typedef struct +{ +  int __m_reserved;               /* Reserved for future use */ +  int __m_count;                  /* Depth of recursive locking */ +  _pthread_descr __m_owner;       /* Owner thread (if recursive or errcheck) */ +  int __m_kind;                   /* Mutex kind: fast, recursive or errcheck */ +  struct _pthread_fastlock __m_lock; /* Underlying fast lock */ +} pthread_mutex_t; + + +/* Attribute for mutex.  */ +typedef struct +{ +  int __mutexkind; +} pthread_mutexattr_t; + + +/* Once-only execution */ +typedef int pthread_once_t; + + +#ifdef __USE_UNIX98 +/* Read-write locks.  */ +typedef struct _pthread_rwlock_t +{ +  struct _pthread_fastlock __rw_lock; /* Lock to guarantee mutual exclusion */ +  int __rw_readers;                   /* Number of readers */ +  _pthread_descr __rw_writer;         /* Identity of writer, or NULL if none */ +  _pthread_descr __rw_read_waiting;   /* Threads waiting for reading */ +  _pthread_descr __rw_write_waiting;  /* Threads waiting for writing */ +  int __rw_kind;                      /* Reader/Writer preference selection */ +  int __rw_pshared;                   /* Shared between processes or not */ +} pthread_rwlock_t; + + +/* Attribute for read-write locks.  */ +typedef struct +{ +  int __lockkind; +  int __pshared; +} pthread_rwlockattr_t; +#endif + + +/* Thread identifiers */ +typedef unsigned long int pthread_t; + +#endif	/* bits/pthreadtypes.h */ diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h b/libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h new file mode 100644 index 000000000..edc69f6cf --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h @@ -0,0 +1,39 @@ +/* Thread package specific definitions of stream lock type. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <pthread.h> + +typedef pthread_mutex_t _IO_lock_t; + +/* We need recursive (counting) mutexes.  */ +#define _IO_lock_initializer PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + + +#define _IO_cleanup_region_start(_fct, _fp) \ +     __libc_cleanup_region_start (_fct, _fp) +#define _IO_cleanup_region_end(_doit) \ +     __libc_cleanup_region_end (_doit) +#define _IO_lock_init(_name) \ +     __libc_lock_init_recursive (_name) +#define _IO_lock_fini(_name) \ +     __libc_lock_fini_recursive (_name) +#define _IO_lock_lock(_name) \ +     __libc_lock_lock (_name) +#define _IO_lock_unlock(_name) \ +     __libc_lock_unlock (_name) diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h b/libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h new file mode 100644 index 000000000..69af8579a --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h @@ -0,0 +1,66 @@ +/* Machine-dependent pthreads configuration and inline functions. +   sparc version. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  int ret; + +  __asm__ __volatile__("ldstub %1,%0" +	: "=r"(ret), "=m"(*spinlock) +	: "m"(*spinlock)); + +  return ret; +} + + +/* Spinlock release; default is just set to zero.  */ +#define RELEASE(spinlock) \ +  __asm__ __volatile__("stbar; stb %1,%0" : "=m"(*(spinlock)) : "r"(0)); + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char * stack_pointer __asm__("%sp"); + + +/* Registers %g6 and %g7 are reserved by the ABI for "system use".  It +   happens that Solaris uses %g6 for the thread pointer -- we do the same.  */ +struct _pthread_descr_struct; +register struct _pthread_descr_struct *__thread_self __asm__("%g6"); + +/* Return the thread descriptor for the current thread.  */ +#define THREAD_SELF  __thread_self + +/* Initialize the thread-unique value.  */ +#define INIT_THREAD_SELF(descr, nr)  (__thread_self = (descr)) + +/* Access to data in the thread descriptor is easy.  */ +#define THREAD_GETMEM(descr, member) __thread_self->member +#define THREAD_GETMEM_NC(descr, member) __thread_self->member +#define THREAD_SETMEM(descr, member, value) __thread_self->member = (value) +#define THREAD_SETMEM_NC(descr, member, value) __thread_self->member = (value) diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h new file mode 100644 index 000000000..2c2770d07 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h @@ -0,0 +1,29 @@ +/* Copyright (C) 1999, 2000 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Jakub Jelinek <jakub@redhat.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#define SIGCONTEXT struct sigcontext * +#define SIGCONTEXT_EXTRA_ARGS +#define GET_PC(__ctx)	((void *) ((__ctx)->si_regs.pc)) +#define ADVANCE_STACK_FRAME(__next) \ +	((void *) (((unsigned *)(__next))+14)) + +#define GET_STACK(__ctx)	((void *) (__ctx)->si_regs.u_regs[14]) +#define GET_FRAME(__ctx)	ADVANCE_STACK_FRAME (GET_STACK(__ctx)) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h b/libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h new file mode 100644 index 000000000..5560003fc --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h @@ -0,0 +1,77 @@ +/* Machine-dependent pthreads configuration and inline functions. +   Sparc v9 version. +   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Richard Henderson <rth@tamu.edu>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If +   not, write to the Free Software Foundation, Inc., +   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ + +#ifndef PT_EI +# define PT_EI extern inline +#endif + + +/* Spinlock implementation; required.  */ +PT_EI int +testandset (int *spinlock) +{ +  int ret; + +  __asm__ __volatile__("ldstub %1,%0" +	: "=r"(ret), "=m"(*spinlock) : "m"(*spinlock)); + +  return ret; +} + + +/* Get some notion of the current stack.  Need not be exactly the top +   of the stack, just something somewhere in the current frame.  */ +#define CURRENT_STACK_FRAME  stack_pointer +register char *stack_pointer __asm__ ("%sp"); + + +/* Registers %g6 and %g7 are reserved by the ABI for "system use".  It +   happens that Solaris uses %g6 for the thread pointer -- we do the same.  */ +struct _pthread_descr_struct; +register struct _pthread_descr_struct *__thread_self __asm__("%g6"); + +/* Return the thread descriptor for the current thread.  */ +#define THREAD_SELF  __thread_self + +/* Initialize the thread-unique value.  */ +#define INIT_THREAD_SELF(descr, nr)  (__thread_self = (descr)) + + +/* Compare-and-swap for semaphores. */ + +#define HAS_COMPARE_AND_SWAP +PT_EI int +__compare_and_swap (long int *p, long int oldval, long int newval) +{ +  long int readval; + +  __asm__ __volatile__ ("casx	[%4], %2, %0" +			: "=r"(readval), "=m"(*p) +			: "r"(oldval), "m"(*p), "r"(p), "0"(newval)); + +  return readval == oldval; +} + +/* Access to data in the thread descriptor is easy.  */ +#define THREAD_GETMEM(descr, member) __thread_self->member +#define THREAD_GETMEM_NC(descr, member) __thread_self->member +#define THREAD_SETMEM(descr, member, value) __thread_self->member = (value) +#define THREAD_SETMEM_NC(descr, member, value) __thread_self->member = (value) diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h new file mode 100644 index 000000000..47e0d27f8 --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h @@ -0,0 +1,32 @@ +/* Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Jakub Jelinek <jj@ultra.linux.cz>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library 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 +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#ifndef STACK_BIAS +#define STACK_BIAS 2047 +#endif +#define SIGCONTEXT struct sigcontext * +#define SIGCONTEXT_EXTRA_ARGS +#define GET_PC(__ctx)	((void *) ((__ctx)->sigc_regs.tpc)) +#define ADVANCE_STACK_FRAME(__next) \ +	((void *) (((unsigned long *) (((unsigned long int) (__next))     \ +					   + STACK_BIAS))+14)) +#define GET_STACK(__ctx)	((void *) ((__ctx)->sigc_regs.u_regs[14])) +#define GET_FRAME(__ctx)	ADVANCE_STACK_FRAME (GET_STACK (__ctx)) +#define CALL_SIGHANDLER(handler, signo, ctx) \ +  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx)) diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h new file mode 100644 index 000000000..545a90b2e --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h @@ -0,0 +1,55 @@ +/* Minimum guaranteed maximum values for system limits.  Linux version. +   Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +/* The kernel header pollutes the namespace with the NR_OPEN symbol. +   Remove this after including the header if necessary.  */ +#ifndef NR_OPEN +# define __undef_NR_OPEN +#endif + +/* The kernel sources contain a file with all the needed information.  */ +#include <linux/limits.h> + +/* Have to remove NR_OPEN?  */ +#ifdef __undef_NR_OPEN +# undef NR_OPEN +# undef __undef_NR_OPEN +#endif + +/* The number of data keys per process.  */ +#define _POSIX_THREAD_KEYS_MAX	128 +/* This is the value this implementation supports.  */ +#define PTHREAD_KEYS_MAX	1024 + +/* Controlling the iterations of destructors for thread-specific data.  */ +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4 +/* Number of iterations this implementation does.  */ +#define PTHREAD_DESTRUCTOR_ITERATIONS	_POSIX_THREAD_DESTRUCTOR_ITERATIONS + +/* The number of threads per process.  */ +#define _POSIX_THREAD_THREADS_MAX	64 +/* This is the value this implementation supports.  */ +#define PTHREAD_THREADS_MAX	1024 + +/* Maximum amount by which a process can descrease its asynchronous I/O +   priority level.  */ +#define AIO_PRIO_DELTA_MAX	20 + +/* Minimum size for a thread.  We are free to choose a reasonable value.  */ +#define PTHREAD_STACK_MIN	16384 diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h new file mode 100644 index 000000000..15683b77e --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h @@ -0,0 +1,110 @@ +/* Define POSIX options for Linux. +   Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef	_POSIX_OPT_H +#define	_POSIX_OPT_H	1 + +/* Job control is supported.  */ +#define	_POSIX_JOB_CONTROL	1 + +/* Processes have a saved set-user-ID and a saved set-group-ID.  */ +#define	_POSIX_SAVED_IDS	1 + +/* Priority scheduling is supported.  */ +#define	_POSIX_PRIORITY_SCHEDULING	1 + +/* Synchronizing file data is supported.  */ +#define	_POSIX_SYNCHRONIZED_IO	1 + +/* The fsync function is present.  */ +#define	_POSIX_FSYNC	1 + +/* Mapping of files to memory is supported.  */ +#define	_POSIX_MAPPED_FILES	1 + +/* Locking of all memory is supported.  */ +#define	_POSIX_MEMLOCK	1 + +/* Locking of ranges of memory is supported.  */ +#define	_POSIX_MEMLOCK_RANGE	1 + +/* Setting of memory protections is supported.  */ +#define	_POSIX_MEMORY_PROTECTION	1 + +/* Implementation supports `poll' function.  */ +#define	_POSIX_POLL	1 + +/* Implementation supports `select' and `pselect' functions.  */ +#define	_POSIX_SELECT	1 + +/* Only root can change owner of file.  */ +#define	_POSIX_CHOWN_RESTRICTED	1 + +/* `c_cc' member of 'struct termios' structure can be disabled by +   using the value _POSIX_VDISABLE.  */ +#define	_POSIX_VDISABLE	'\0' + +/* Filenames are not silently truncated.  */ +#define	_POSIX_NO_TRUNC	1 + +/* X/Open realtime support is available.  */ +#define _XOPEN_REALTIME	1 + +/* X/Open realtime thread support is available.  */ +#define _XOPEN_REALTIME_THREADS	1 + +/* XPG4.2 shared memory is supported.  */ +#define	_XOPEN_SHM	1 + +/* Tell we have POSIX threads.  */ +#define _POSIX_THREADS	1 + +/* We have the reentrant functions described in POSIX.  */ +#define _POSIX_REENTRANT_FUNCTIONS      1 +#define _POSIX_THREAD_SAFE_FUNCTIONS	1 + +/* We provide priority scheduling for threads.  */ +#define	_POSIX_THREAD_PRIORITY_SCHEDULING	1 + +/* We support user-defined stack sizes.  */ +#define _POSIX_THREAD_ATTR_STACKSIZE	1 + +/* We support user-defined stacks.  */ +#define _POSIX_THREAD_ATTR_STACKADDR	1 + +/* We support POSIX.1b semaphores, but only the non-shared form for now.  */ +/*#define _POSIX_SEMAPHORES	1	XXX We are not quite there now.  */ + +/* Real-time signals are supported.  */ +#define _POSIX_REALTIME_SIGNALS	1 + +/* We support asynchronous I/O.  */ +#define _POSIX_ASYNCHRONOUS_IO	1 +/* Alternative name for Unix98.  */ +#define _LFS_ASYNCHRONOUS_IO	1 + +/* The LFS support in asynchronous I/O is also available.  */ +#define _LFS64_ASYNCHRONOUS_IO	1 + +/* The rest of the LFS is also available.  */ +#define _LFS_LARGEFILE		1 +#define _LFS64_LARGEFILE	1 +#define _LFS64_STDIO		1 + +#endif /* posix_opt.h */ diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h new file mode 100644 index 000000000..c9b1dcf1b --- /dev/null +++ b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h @@ -0,0 +1,37 @@ +/* Signal handling function for threaded programs. +   Copyright (C) 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef _BITS_SIGTHREAD_H  +#define _BITS_SIGTHREAD_H	1 + +#if !defined _SIGNAL_H && !defined _PTHREAD_H +# error "Never include this file directly.  Use <pthread.h> instead" +#endif + +/* Functions for handling signals. */ + +/* Modify the signal mask for the calling thread.  The arguments have +   the same meaning as for sigprocmask(2). */ +extern int pthread_sigmask __P ((int __how, __const __sigset_t *__newmask, +				 __sigset_t *__oldmask)); + +/* Send signal SIGNO to the given thread. */ +extern int pthread_kill __P ((pthread_t __thread, int __signo)); + +#endif	/* bits/sigthread.h */ diff --git a/libpthread/linuxthreads/testrtsig.h b/libpthread/linuxthreads/testrtsig.h new file mode 100644 index 000000000..fb8b011f0 --- /dev/null +++ b/libpthread/linuxthreads/testrtsig.h @@ -0,0 +1,40 @@ +/* Test whether RT signals are really available. +   Copyright (C) 1997 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <limits.h> +#include <string.h> +#include <sys/utsname.h> + +static int +kernel_has_rtsig (void) +{ +  return 0; /* hacked to test old uClibc that doesn't know about RT signals. 0.9.9 should work with RT signals. Make this proper in the end! */ +#ifdef RTSIG_MAX +  return 1; +#else +  return 0; +#endif + +/* +  struct utsname name; + +  return uname (&name) == 0 && __strverscmp (name.release, "2.1.70") >= 0; +*/ +} diff --git a/libpthread/linuxthreads/weaks.c b/libpthread/linuxthreads/weaks.c new file mode 100644 index 000000000..5281a2a5b --- /dev/null +++ b/libpthread/linuxthreads/weaks.c @@ -0,0 +1,120 @@ +/* The weak pthread functions for Linux. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +extern int __pthread_return_0 __P ((void)); +extern int __pthread_return_1 __P ((void)); +extern void __pthread_return_void __P ((void)); + +/* NOTE: these require a strong alias in attr.c. I don't think we need this + * versioning stuff with uClibc. -StS + */ + +/* Those are pthread functions which return 0 if successful. */ +//#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING +//weak_alias (__pthread_return_0, __libc_pthread_attr_init_2_0) +//symbol_version (__libc_pthread_attr_init_2_0, pthread_attr_init, GLIBC_2.0); +//weak_alias (__pthread_return_0, __libc_pthread_attr_init_2_1) +//default_symbol_version (__libc_pthread_attr_init_2_1, pthread_attr_init, +//			GLIBC_2.1); +//#else +weak_alias (__pthread_return_0, pthread_attr_init) +//#endif +weak_alias (__pthread_return_0, pthread_attr_destroy) +weak_alias (__pthread_return_0, pthread_attr_setdetachstate) +weak_alias (__pthread_return_0, pthread_attr_getdetachstate) +weak_alias (__pthread_return_0, pthread_attr_setschedparam) +weak_alias (__pthread_return_0, pthread_attr_getschedparam) +weak_alias (__pthread_return_0, pthread_attr_setschedpolicy) +weak_alias (__pthread_return_0, pthread_attr_getschedpolicy) +weak_alias (__pthread_return_0, pthread_attr_setinheritsched) +weak_alias (__pthread_return_0, pthread_attr_getinheritsched) +weak_alias (__pthread_return_0, pthread_attr_setscope) +weak_alias (__pthread_return_0, pthread_attr_getscope) +weak_alias (__pthread_return_0, pthread_attr_setstackaddr) +weak_alias (__pthread_return_0, pthread_attr_getstackaddr) +weak_alias (__pthread_return_0, pthread_attr_setstacksize) +weak_alias (__pthread_return_0, pthread_attr_getstacksize) +weak_alias (__pthread_return_0, pthread_mutex_init) +weak_alias (__pthread_return_0, pthread_mutex_destroy) +weak_alias (__pthread_return_0, pthread_mutex_lock) +weak_alias (__pthread_return_0, pthread_mutex_trylock) +weak_alias (__pthread_return_0, pthread_mutex_unlock) +weak_alias (__pthread_return_0, pthread_mutexattr_init) +weak_alias (__pthread_return_0, pthread_mutexattr_destroy) +weak_alias (__pthread_return_0, pthread_mutexattr_settype) +weak_alias (__pthread_return_0, pthread_mutexattr_gettype) +weak_alias (__pthread_return_0, pthread_condattr_init) +weak_alias (__pthread_return_0, pthread_condattr_destroy) +weak_alias (__pthread_return_0, pthread_setschedparam) +weak_alias (__pthread_return_0, pthread_getschedparam) +weak_alias (__pthread_return_0, pthread_getcancelstate) +weak_alias (__pthread_return_0, pthread_setcancelstate) +weak_alias (__pthread_return_0, pthread_setcanceltype) +weak_alias (__pthread_return_0, pthread_setconcurrency) +weak_alias (__pthread_return_0, pthread_getconcurrency) +weak_alias (__pthread_return_0, pthread_self) +weak_alias (__pthread_return_0, pthread_cond_init) +weak_alias (__pthread_return_0, pthread_cond_destroy) +weak_alias (__pthread_return_0, pthread_cond_wait) +weak_alias (__pthread_return_0, pthread_cond_timedwait) +weak_alias (__pthread_return_0, pthread_cond_signal) +weak_alias (__pthread_return_0, pthread_cond_broadcast) +weak_alias (__pthread_return_0, pthread_rwlock_init) +weak_alias (__pthread_return_0, pthread_rwlock_destroy) +weak_alias (__pthread_return_0, pthread_rwlock_rdlock) +weak_alias (__pthread_return_0, pthread_rwlock_wrlock) +weak_alias (__pthread_return_0, pthread_rwlock_tryrdlock) +weak_alias (__pthread_return_0, pthread_rwlock_trywrlock) +weak_alias (__pthread_return_0, pthread_rwlock_unlock) +weak_alias (__pthread_return_0, pthread_rwlockattr_init) +weak_alias (__pthread_return_0, pthread_rwlockattr_destroy) +weak_alias (__pthread_return_0, pthread_rwlockattr_setpshared) +weak_alias (__pthread_return_0, pthread_rwlockattr_getpshared) + + +/* Those are pthread functions which return 1 if successful. */ +weak_alias (__pthread_return_1, pthread_equal) + +/* pthread_exit () is a special case. */ +void weak_function +pthread_exit (void *retval) +{ +  exit (EXIT_SUCCESS); +} + +int +__pthread_return_0 (void) +{ +  return 0; +} + +int +__pthread_return_1 (void) +{ +  return 1; +} + +void +__pthread_return_void (void) +{ +} diff --git a/libpthread/linuxthreads/wrapsyscall.c b/libpthread/linuxthreads/wrapsyscall.c new file mode 100644 index 000000000..3b9ada160 --- /dev/null +++ b/libpthread/linuxthreads/wrapsyscall.c @@ -0,0 +1,178 @@ +/* Wrapper arpund system calls to provide cancelation points. +   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#define __FORCE_GLIBC +#include <features.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <pthread.h> +#include <unistd.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdlib.h> +#include <termios.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/socket.h> + + +#ifndef PIC +/* We need a hook to force this file to be linked in when static +   libpthread is used.  */ +const int __pthread_provide_wrappers = 0; +#endif + + +#define CANCELABLE_SYSCALL(res_type, name, param_list, params) \ +res_type __libc_##name param_list;					      \ +res_type								      \ +name param_list								      \ +{									      \ +  res_type result;							      \ +  int oldtype;								      \ +  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);	      \ +  result = __libc_##name params;					      \ +  pthread_setcanceltype (oldtype, NULL);				      \ +  return result;							      \ +} + +#define CANCELABLE_SYSCALL_VA(res_type, name, param_list, params, last_arg) \ +res_type __libc_##name param_list;					      \ +res_type								      \ +name param_list								      \ +{									      \ +  res_type result;							      \ +  int oldtype;								      \ +  va_list ap;								      \ +  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);	      \ +  va_start (ap, last_arg);						      \ +  result = __libc_##name params;					      \ +  va_end (ap);								      \ +  pthread_setcanceltype (oldtype, NULL);				      \ +  return result;							      \ +} + + +/* close(2).  */ +CANCELABLE_SYSCALL (int, close, (int fd), (fd)) + + +/* fcntl(2).  */ +CANCELABLE_SYSCALL_VA (int, fcntl, (int fd, int cmd, ...), +		       (fd, cmd, va_arg (ap, long int)), cmd) + + +/* fsync(2).  */ +CANCELABLE_SYSCALL (int, fsync, (int fd), (fd)) + + +/* lseek(2).  */ +CANCELABLE_SYSCALL (off_t, lseek, (int fd, off_t offset, int whence), +		    (fd, offset, whence)) + + +/* msync(2).  */ +/* This syscall not implemented in uClibc +CANCELABLE_SYSCALL (int, msync, (__ptr_t addr, size_t length, int flags), +		    (addr, length, flags)) +*/ + +/* nanosleep(2).  */ +CANCELABLE_SYSCALL (int, nanosleep, (const struct timespec *requested_time, +				     struct timespec *remaining), +		    (requested_time, remaining)) + + +/* open(2).  */ +CANCELABLE_SYSCALL_VA (int, open, (const char *pathname, int flags, ...), +		       (pathname, flags, va_arg (ap, mode_t)), flags) + + +/* pause(2).  */ +CANCELABLE_SYSCALL (int, pause, (void), ()) + + +/* read(2).  */ +CANCELABLE_SYSCALL (ssize_t, read, (int fd, void *buf, size_t count), +		    (fd, buf, count)) + + +/* system(3).  */ +CANCELABLE_SYSCALL (int, system, (const char *line), (line)) + +/* tcdrain(2).  */ +CANCELABLE_SYSCALL (int, tcdrain, (int fd), (fd)) + +/* wait(2).  */ +CANCELABLE_SYSCALL (__pid_t, wait, (__WAIT_STATUS_DEFN stat_loc), (stat_loc)) + + +/* waitpid(2).  */ +CANCELABLE_SYSCALL (__pid_t, waitpid, (__pid_t pid, int *stat_loc, +				       int options), +		    (pid, stat_loc, options)) + + +/* write(2).  */ +CANCELABLE_SYSCALL (ssize_t, write, (int fd, const void *buf, size_t n), +		    (fd, buf, n)) + + +/* The following system calls are thread cancellation points specified +   in XNS.  */ + +/* accept(2).  */ +CANCELABLE_SYSCALL (int, accept, (int fd, __SOCKADDR_ARG addr, +				  socklen_t *addr_len), +		    (fd, addr, addr_len)) + +/* connect(2).  */ +CANCELABLE_SYSCALL (int, connect, (int fd, __CONST_SOCKADDR_ARG addr, +				     socklen_t len), +		    (fd, addr, len)) + +/* recv(2).  */ +CANCELABLE_SYSCALL (int, recv, (int fd, __ptr_t buf, size_t n, int flags), +		    (fd, buf, n, flags)) + +/* recvfrom(2).  */ +CANCELABLE_SYSCALL (int, recvfrom, (int fd, __ptr_t buf, size_t n, int flags, +				    __SOCKADDR_ARG addr, socklen_t *addr_len), +		    (fd, buf, n, flags, addr, addr_len)) + +/* recvmsg(2).  */ +CANCELABLE_SYSCALL (int, recvmsg, (int fd, struct msghdr *message, int flags), +		    (fd, message, flags)) + +/* send(2).  */ +CANCELABLE_SYSCALL (int, send, (int fd, const __ptr_t buf, size_t n, +				int flags), +		    (fd, buf, n, flags)) + +/* sendmsg(2).  */ +CANCELABLE_SYSCALL (int, sendmsg, (int fd, const struct msghdr *message, +				   int flags), +		    (fd, message, flags)) + +/* sendto(2).  */ +CANCELABLE_SYSCALL (int, sendto, (int fd, const __ptr_t buf, size_t n, +				  int flags, __CONST_SOCKADDR_ARG addr, +				  socklen_t addr_len), +		    (fd, buf, n, flags, addr, addr_len)) diff --git a/libpthread/linuxthreads_db/ChangeLog b/libpthread/linuxthreads_db/ChangeLog new file mode 100644 index 000000000..4d29ae0bb --- /dev/null +++ b/libpthread/linuxthreads_db/ChangeLog @@ -0,0 +1,208 @@ +2000-01-19  Ulrich Drepper  <drepper@cygnus.com> + +	* td_thr_getgregs.c: Correct size parameter of memset call. + +1999-12-02  Ulrich Drepper  <drepper@cygnus.com> + +	* proc_service.h: Fix typoes in last added declaractions. + +1999-12-01  Ulrich Drepper  <drepper@cygnus.com> + +	* proc_service.h: Add ps_pstop, ps_pcontinue, ps_lstop, and +	ps_lcontinue prototypes. + +1999-11-23  Ulrich Drepper  <drepper@cygnus.com> + +	* Makefile: Correct dependency for shared object. + +1999-11-22  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_map_lwp2thr.c: Add missing brace in comparison. + +	* thread_dbP.h (LOG): Only print message if __td_debug is nonzero. +	* td_init.c: Add __td_debug. + +1999-11-12  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_thr_iter.c: Start copying list of descriptors from right +	position in target process. + +	* td_ta_thr_iter.c: Fix loop starting point over all but main and +	manager thread. + +	* td_ta_thr_iter.c: Read descriptors for main and manager thread +	special since after this we can assume that no new threads will be +	created anymore (at least in the gdb implementation). + +	* Makefile: Define version correctly. + +1999-11-10  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_map_lwp2thr.c: If p_pid field is zero, this is before the +	thread library is initialized and we get the PID from the +	debugger. + +1999-11-08  Ulrich Drepper  <drepper@cygnus.com> + +	* td_thr_get_info.c: Make sure ti_lid is never zero. + +	* proc_service.h: Add ps_getpid prototype. + +1999-11-03  Ulrich Drepper  <drepper@cygnus.com> + +	* thread_dbP.h (ta_ok): New function. +	* td_ta_new.c: Add new handle to list. +	* td_ta_delete.c: Remove handle from list. +	* td_ta_clear_event.c: Use ta_ok to check for correct ta parameter. +	* td_ta_enable_stats.c: Likewise. +	* td_ta_event_addr.c: Likewise. +	* td_ta_event_getmsg.c: Likewise. +	* td_ta_get_nthreads.c: Likewise. +	* td_ta_get_ph.c: Likewise. +	* td_ta_get_stats.c: Likewise. +	* td_ta_map_id2thr.c: Likewise. +	* td_ta_map_lwp2thr.c: Likewise. +	* td_ta_reset_stats.c: Likewise. +	* td_ta_set_event.c: Likewise. +	* td_ta_setconcurrency.c: Likewise. +	* td_ta_thr_iter.c: Likewise. + +	* td_ta_tsd_iter.c: Optimize memory retrieving. + +	* Versions: New file. + +	* td_thr_get_info.c (td_thr_get_info): Initialize ti_traceme. + +1999-11-02  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_thr_iter.c (td_ta_thr_iter): Optimize a bit.  Read all +	handles at once. + +	* thread_dbP.h (struct th_thragent): Add pthread_handle_num. +	* td_ta_new.c: Initialize pthread_handle_num. +	* td_ta_event_getmsg.c: If last event was already reported search +	for another unreported event. + +	* td_thr_get_info.c (td_thr_get_info): Initialize ti_events. + +	* Makefile (libthread_db-routines): Add td_ta_set_event, +	td_ta_event_getmsg, and td_ta_clear_event. +	* td_ta_clear_event.c: New file. +	* td_ta_event_getmsg.c: New file. +	* td_ta_new.c: Get address of __pthread_last_event in target. +	* td_ta_set_event.c: Don't overwrite old mask, set additional bits. +	* td_thr_set_event.c: Likewise. +	* td_thr_clear_event.c: Implement. +	* thread_db.h: Declare td_ta_clear_event and td_ta_event_getmsg. +	* thread_dbP.h (struct td_thragent): Add pthread_last_event. + +	* td_ta_new.c: Don't test for __pthread_threads_debug.  Get address +	of __pthread_threads_events and fail if this is not possible. +	* td_ta_event_addr.c: Implement. +	* td_thr_event_enable.c: Implement. +	* td_thr_event_getmsg.c: Implement. +	* td_thr_set_event.c: Implement. +	* td_ta_set_event.c: New file. +	* thread_db.h (td_eventbuf_t): Define. +	Declare td_ta_set_event. +	* thread_dbP.h (struct td_thragent): Add pthread_threads_eventsp. + +	* td_thr_getfpregs.c: For terminated threads return empty structure. +	* td_thr_getgregs.c: Likewise. +	* td_thr_setfpregs.c: Likewise. +	* td_thr_setgregs.c: Likewise. + +1999-11-01  Ulrich Drepper  <drepper@cygnus.com> + +	* thread_db.h: Shuffle types around to make things work for gdb. +	* thread_dbP.h: Include proc_service.h before thread_db.h. + +	* thread_db.h: It's TD_NOLIBTHREAD, not TD_LIBTHREAD. +	* td_ta_new.c: Likewise. + +1999-10-14  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_new.c: p_startfct does not exist anymore. + +	* td_thr_get_info.c: Always initialize start function. + +	* td_ta_thr_iter.c: Don't return threads which exited (but are not +	joined). + +	* td_thr_validate.c: Don't skip manager thread. + +1999-10-13  Ulrich Drepper  <drepper@cygnus.com> + +	* td_ta_thr_iter.c: Use size of descriptor from *TA. +	Don't return manager thread before it's actually running. +	Actually use state parameter to distingusih at least a few states. + +	* td_thr_get_info.c: Handle manager thread special.  Fill in ti_lid, +	ti_state, and ti_startfunc fields. + +1999-10-12  Andreas Jaeger  <aj@suse.de> + +	* thread_dbP.h: Include <string.h> for strlen declaration.  Remove +	__libc_write prototype since this is already declared in +	linuxthreads/internals.h. + +1999-10-11  Ulrich Drepper  <drepper@cygnus.com> + +	* thread_db.h: Fix comment for ti_type. + +	* td_thr_get_info.c: Initialize ti_type field. + +	* td_ta_thr_iter.c: Also report the manager thread. + +1999-10-08  Andreas Jaeger  <aj@suse.de> + +	* thread_db.h: Fix typos in comments. + +	* td_ta_get_nthreads.c (td_ta_get_nthreads): Don't hardcode +	libpthread library name, get it from <gnu/lib-names.h> instead. +	* td_ta_new.c (td_ta_new): Likewise. + +1999-10-08  Ulrich Drepper  <drepper@cygnus.com> + +	* shlib-versions: New file. + +1999-10-07  Ulrich Drepper  <drepper@cygnus.com> + +	* Makefile: New file. +	* proc_service.h: New file. +	* td_init.c: New file. +	* td_log.c: New file. +	* td_ta_delete.c: New file. +	* td_ta_enable_stats.c: New file. +	* td_ta_event_addr.c: New file. +	* td_ta_get_nthreads.c: New file. +	* td_ta_get_ph.c: New file. +	* td_ta_get_stats.c: New file. +	* td_ta_map_id2thr.c: New file. +	* td_ta_map_lwp2thr.c: New file. +	* td_ta_new.c: New file. +	* td_ta_reset_stats.c: New file. +	* td_ta_setconcurrency.c: New file. +	* td_ta_thr_iter.c: New file. +	* td_ta_tsd_iter.c: New file. +	* td_thr_clear_event.c: New file. +	* td_thr_dbresume.c: New file. +	* td_thr_dbsuspend.c: New file. +	* td_thr_event_enable.c: New file. +	* td_thr_event_getmsg.c: New file. +	* td_thr_get_info.c: New file. +	* td_thr_getfpregs.c: New file. +	* td_thr_getgregs.c: New file. +	* td_thr_getxregs.c: New file. +	* td_thr_getxregsize.c: New file. +	* td_thr_set_event.c: New file. +	* td_thr_setfpregs.c: New file. +	* td_thr_setgregs.c: New file. +	* td_thr_setprio.c: New file. +	* td_thr_setsigpending.c: New file. +	* td_thr_setxregs.c: New file. +	* td_thr_sigsetmask.c: New file. +	* td_thr_tsd.c: New file. +	* td_thr_validate.c: New file. +	* thread_db.h: New file. +	* thread_dbP.h: New file. diff --git a/libpthread/linuxthreads_db/Makefile b/libpthread/linuxthreads_db/Makefile new file mode 100644 index 000000000..eea12c7a4 --- /dev/null +++ b/libpthread/linuxthreads_db/Makefile @@ -0,0 +1,68 @@ +# Makefile for uClibc's pthread library +# +# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Library 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 Library General Public License for more +# details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Makefile for uClibc + +TOPDIR=../../ +include $(TOPDIR)Rules.mak + +#Adjust the soname version to avoid namespace collisions with glibc's libpthread +PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION) +LIBPTHREAD=../libpthread.a + +# set up system dependencies include dirs (NOTE: order matters!) +PTDIR = $(TOPDIR)libpthread/linuxthreads/ +SYSDEPINC = -I$(PTDIR)sysdeps/unix/sysv/linux \ +            -I$(PTDIR)sysdeps/pthread \ +            -I$(PTDIR)sysdeps/unix/sysv \ +            -I$(PTDIR)sysdeps/unix/unix \ +            -I$(PTDIR)sysdeps/$(TARGET_ARCH) \ +            -I$(PTDIR)sysdeps \ +            -I$(TOPDIR)libc/sysdeps/linux/$(TARGET_ARCH) +CFLAGS += $(SYSDEPINC) -DLIBPTHREAD_SO="\"libpthread.so.$(PT_VERSION)\"" -D_GNU_SOURCE + +CSRC=td_init.c td_log.c td_ta_clear_event.c td_ta_delete.c \ +	td_ta_enable_stats.c td_ta_event_addr.c td_ta_event_getmsg.c \ +	td_ta_get_nthreads.c td_ta_get_ph.c td_ta_get_stats.c \ +	td_ta_map_id2thr.c td_ta_map_lwp2thr.c td_ta_new.c td_ta_reset_stats.c \ +	td_ta_set_event.c td_ta_setconcurrency.c td_ta_thr_iter.c \ +	td_ta_tsd_iter.c td_thr_clear_event.c td_thr_dbresume.c \ +	td_thr_dbsuspend.c td_thr_event_enable.c td_thr_event_getmsg.c \ +	td_thr_get_info.c td_thr_getfpregs.c td_thr_getgregs.c \ +	td_thr_getxregs.c td_thr_getxregsize.c td_thr_set_event.c \ +	td_thr_setfpregs.c td_thr_setgregs.c td_thr_setprio.c \ +	td_thr_setsigpending.c td_thr_setxregs.c td_thr_sigsetmask.c \ +	td_thr_tsd.c td_thr_validate.c +COBJS=$(patsubst %.c,%.o, $(CSRC)) +OBJS=$(COBJS) + +all: $(OBJS) $(LIBPTHREAD) + +$(LIBPTHREAD): ar-target + +ar-target: $(OBJS) +	$(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS) + +$(COBJS): %.o : %.c +	$(CC) $(CFLAGS) -c $< -o $@ +	$(STRIPTOOL) -x -R .note -R .comment $*.o + +clean: +	rm -f *.[oa] *~ core + + + diff --git a/libpthread/linuxthreads_db/Versions b/libpthread/linuxthreads_db/Versions new file mode 100644 index 000000000..83b30ee6e --- /dev/null +++ b/libpthread/linuxthreads_db/Versions @@ -0,0 +1,15 @@ +libthread_db { +  GLIBC_2.1.3 { +    # t* +    td_init; td_log; td_ta_clear_event; td_ta_delete; td_ta_enable_stats; +    td_ta_event_addr; td_ta_event_getmsg; td_ta_get_nthreads; td_ta_get_ph; +    td_ta_get_stats; td_ta_map_id2thr; td_ta_map_lwp2thr; td_ta_new; +    td_ta_reset_stats; td_ta_set_event; td_ta_setconcurrency; +    td_ta_thr_iter; td_ta_tsd_iter; td_thr_clear_event; td_thr_dbresume; +    td_thr_dbsuspend; td_thr_event_enable; td_thr_event_getmsg; +    td_thr_get_info; td_thr_getfpregs; td_thr_getgregs; td_thr_getxregs; +    td_thr_getxregsize; td_thr_set_event; td_thr_setfpregs; td_thr_setgregs; +    td_thr_setprio; td_thr_setsigpending; td_thr_setxregs; td_thr_sigsetmask; +    td_thr_tsd; td_thr_validate; +  } +} diff --git a/libpthread/linuxthreads_db/proc_service.h b/libpthread/linuxthreads_db/proc_service.h new file mode 100644 index 000000000..8f9a7d98f --- /dev/null +++ b/libpthread/linuxthreads_db/proc_service.h @@ -0,0 +1,70 @@ +/* Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +/* The definitions in this file must correspond to those in the debugger.  */ +#include <sys/procfs.h> + +typedef enum +{ +  PS_OK,          /* generic "call succeeded" */ +  PS_ERR,         /* generic. */ +  PS_BADPID,      /* bad process handle */ +  PS_BADLID,      /* bad lwp identifier */ +  PS_BADADDR,     /* bad address */ +  PS_NOSYM,       /* p_lookup() could not find given symbol */ +        PS_NOFREGS +  /* +   * FPU register set not available for given +   * lwp +   */ +}       ps_err_e; + + +struct ps_prochandle;		/* user defined. */ + + +extern ps_err_e ps_pdread(struct ps_prochandle *, +                        psaddr_t, void *, size_t); +extern ps_err_e ps_pdwrite(struct ps_prochandle *, +                        psaddr_t, const void *, size_t); +extern ps_err_e ps_ptread(struct ps_prochandle *, +                        psaddr_t, void *, size_t); +extern ps_err_e ps_ptwrite(struct ps_prochandle *, +                        psaddr_t, const void *, size_t); + +extern ps_err_e ps_pglobal_lookup(struct ps_prochandle *, +        const char *object_name, const char *sym_name, psaddr_t *sym_addr); + + +extern ps_err_e ps_lgetregs(struct ps_prochandle *, +                        lwpid_t, prgregset_t); +extern ps_err_e ps_lsetregs(struct ps_prochandle *, +                        lwpid_t, const prgregset_t); +extern ps_err_e ps_lgetfpregs(struct ps_prochandle *, +                        lwpid_t, prfpregset_t *); +extern ps_err_e ps_lsetfpregs(struct ps_prochandle *, +                        lwpid_t, const prfpregset_t *); + +extern pid_t ps_getpid (struct ps_prochandle *); + + +extern ps_err_e ps_pstop (const struct ps_prochandle *); +extern ps_err_e ps_pcontinue (const struct ps_prochandle *); + +extern ps_err_e ps_lstop (const struct ps_prochandle *, lwpid_t); +extern ps_err_e ps_lcontinue (const struct ps_prochandle *, lwpid_t); diff --git a/libpthread/linuxthreads_db/td_init.c b/libpthread/linuxthreads_db/td_init.c new file mode 100644 index 000000000..683aec427 --- /dev/null +++ b/libpthread/linuxthreads_db/td_init.c @@ -0,0 +1,32 @@ +/* Initialization function of thread debugger support library. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + +int __td_debug; + + +td_err_e +td_init (void) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_log.c b/libpthread/linuxthreads_db/td_log.c new file mode 100644 index 000000000..0c4a3670f --- /dev/null +++ b/libpthread/linuxthreads_db/td_log.c @@ -0,0 +1,32 @@ +/* Noop, left for historical reasons. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_log (void) +{ +  /* This interface is deprecated in the Sun interface.  We provide it +     for compatibility but don't do anyhting ourself.  We might in +     future do some logging if this seems reasonable.  */ +  LOG (__FUNCTION__); +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_clear_event.c b/libpthread/linuxthreads_db/td_ta_clear_event.c new file mode 100644 index 000000000..02d83360f --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_clear_event.c @@ -0,0 +1,53 @@ +/* Globally disable events. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_clear_event (ta, event) +     const td_thragent_t *ta; +     td_thr_events_t *event; +{ +  td_thr_events_t old_event; +  int i; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdread (ta->ph, ta->pthread_threads_eventsp, +		 &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Remove the set bits in.  */ +  for (i = 0; i < TD_EVENTSIZE; ++i) +    old_event.event_bits[i] &= ~event->event_bits[i]; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdwrite (ta->ph, ta->pthread_threads_eventsp, +		  &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_delete.c b/libpthread/linuxthreads_db/td_ta_delete.c new file mode 100644 index 000000000..e98357766 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_delete.c @@ -0,0 +1,58 @@ +/* Detach to target process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stdlib.h> + +#include "thread_dbP.h" + + +td_err_e +td_ta_delete (td_thragent_t *ta) +{ +  LOG (__FUNCTION__); + +  /* Safety check.  */ +  if (ta == NULL || __td_agent_list == NULL) +    return TD_BADTA; + +  /* Remove the handle from the list.  */ +  if (ta == __td_agent_list->ta) +    /* It's the first element of the list.  */ +    __td_agent_list = __td_agent_list->next; +  else +    { +      /* We have to search for it.  */ +      struct agent_list *runp = __td_agent_list; + +      while (runp->next != NULL && runp->next->ta != ta) +	runp = runp->next; + +      if (runp->next == NULL) +	/* It's not a valid decriptor since it is not in the list.  */ +	return TD_BADTA; + +      runp->next = runp->next->next; +    } + +  /* The handle was allocated in `td_ta_new'.  */ +  free (ta); + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_enable_stats.c b/libpthread/linuxthreads_db/td_ta_enable_stats.c new file mode 100644 index 000000000..abf4d200a --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_enable_stats.c @@ -0,0 +1,35 @@ +/* Enable collection of statistics for process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_enable_stats (const td_thragent_t *ta, int enable) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_event_addr.c b/libpthread/linuxthreads_db/td_ta_event_addr.c new file mode 100644 index 000000000..e610e4422 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_event_addr.c @@ -0,0 +1,75 @@ +/* Get event address. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +//#include <gnu/lib-names.h> + +#include "thread_dbP.h" + + +td_err_e +td_ta_event_addr (const td_thragent_t *ta, td_event_e event, td_notify_t *addr) +{ +  td_err_e res = TD_NOEVENT; +  const char *symbol = NULL; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  switch (event) +    { +    case TD_CREATE: +      symbol = "__linuxthreads_create_event"; +      break; + +    case TD_DEATH: +      symbol = "__linuxthreads_death_event"; +      break; + +    case TD_REAP: +      symbol = "__linuxthreads_reap_event"; +      break; + +    default: +      /* Event cannot be handled.  */ +      break; +    } + +  /* Now get the address.  */ +  if (symbol != NULL) +    { +      psaddr_t taddr; + +      if (ps_pglobal_lookup (ta->ph, LIBPTHREAD_SO, symbol, &taddr) == PS_OK) +	{ +	  /* Success, we got the address.  */ +	  addr->type = NOTIFY_BPT; +	  addr->u.bptaddr = taddr; + +	  res = TD_OK; +	} +      else +	res = TD_ERR; +    } + +  return res; +} diff --git a/libpthread/linuxthreads_db/td_ta_event_getmsg.c b/libpthread/linuxthreads_db/td_ta_event_getmsg.c new file mode 100644 index 000000000..4c635dc10 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_event_getmsg.c @@ -0,0 +1,128 @@ +/* Retrieve event. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> +#include <string.h> + +#include "thread_dbP.h" + + +td_err_e +td_ta_event_getmsg (const td_thragent_t *ta, td_event_msg_t *msg) +{ +  /* XXX I cannot think of another way but using a static variable.  */ +  static td_thrhandle_t th; +  td_eventbuf_t event; +  psaddr_t addr; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Get the pointer to the thread descriptor with the last event.  */ +  if (ps_pdread (ta->ph, ta->pthread_last_event, +		 &addr, sizeof (void *)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* If the pointer is NULL no event occurred.  */ +  if (addr == 0) +    return TD_NOMSG; + +  /* Read the even structure from the target.  */ +  if (ps_pdread (ta->ph, +		 ((char *) addr +		  + offsetof (struct _pthread_descr_struct, p_eventbuf)), +		 &event, sizeof (td_eventbuf_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Check whether an event occurred.  */ +  if (event.eventnum == TD_EVENT_NONE) +    { +      /* Oh well, this means the last event was already read.  So +	 we have to look for any other event.  */ +      struct pthread_handle_struct handles[ta->pthread_threads_max]; +      int num; +      int i; + +      /* Read the number of currently active threads.  */ +      if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int)) +	  != PS_OK) +	return TD_ERR;	/* XXX Other error value?  */ + +      /* Now read the handles.  */ +      if (ps_pdread (ta->ph, ta->handles, handles, +		     ta->pthread_threads_max * sizeof (handles[0])) != PS_OK) +	return TD_ERR;	/* XXX Other error value?  */ + +      for (i = 0; i < ta->pthread_threads_max && num > 0; ++i) +	{ +	  if (handles[i].h_descr == NULL) +	    /* No entry here.  */ +	    continue; + +	  /* First count this active thread.  */ +	  --num; + +	  if (handles[i].h_descr == addr) +	    /* We already handled this.  */ +	    continue; + +	  /* Read the event data for this thread.  */ +	  if (ps_pdread (ta->ph, +			 ((char *) handles[i].h_descr +			  + offsetof (struct _pthread_descr_struct, +				      p_eventbuf)), +			 &event, sizeof (td_eventbuf_t)) != PS_OK) +	    return TD_ERR; + +	  if (event.eventnum != TD_EVENT_NONE) +	    { +	      /* We found a thread with an unreported event.  */ +	      addr = handles[i].h_descr; +	      break; +	    } +	} + +      /* If we haven't found any other event signal this to the user.  */ +      if (event.eventnum == TD_EVENT_NONE) +	return TD_NOMSG; +    } + +  /* Generate the thread descriptor.  */ +  th.th_ta_p = (td_thragent_t *) ta; +  th.th_unique = addr; + +  /* Fill the user's data structure.  */ +  msg->event = event.eventnum; +  msg->th_p = &th; +  msg->msg.data = (uintptr_t) event.eventdata; + +  /* And clear the event message in the target.  */ +  memset (&event, '\0', sizeof (td_eventbuf_t)); +  if (ps_pdwrite (ta->ph, +		  ((char *) addr +		   + offsetof (struct _pthread_descr_struct, p_eventbuf)), +		  &event, sizeof (td_eventbuf_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_get_nthreads.c b/libpthread/linuxthreads_db/td_ta_get_nthreads.c new file mode 100644 index 000000000..7800487a8 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_get_nthreads.c @@ -0,0 +1,44 @@ +/* Get the number of threads in the process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" +//#include <gnu/lib-names.h> + +td_err_e +td_ta_get_nthreads (const td_thragent_t *ta, int *np) +{ +  psaddr_t addr; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Access the variable `__pthread_handles_num'.  */ +  if (ps_pglobal_lookup (ta->ph, LIBPTHREAD_SO, "__pthread_handles_num", +		         &addr) != PS_OK) +     return TD_ERR;	/* XXX Other error value?  */ + +  if (ps_pdread (ta->ph, addr, np, sizeof (int)) != PS_OK) +     return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_get_ph.c b/libpthread/linuxthreads_db/td_ta_get_ph.c new file mode 100644 index 000000000..b748916ba --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_get_ph.c @@ -0,0 +1,36 @@ +/* Get external process handle. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_get_ph (const td_thragent_t *ta, struct ps_prochandle **ph) +{ +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  *ph = ta->ph; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_get_stats.c b/libpthread/linuxthreads_db/td_ta_get_stats.c new file mode 100644 index 000000000..1741d8145 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_get_stats.c @@ -0,0 +1,35 @@ +/* Retrieve statistics for process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_get_stats (const td_thragent_t *ta, td_ta_stats_t *statsp) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_map_id2thr.c b/libpthread/linuxthreads_db/td_ta_map_id2thr.c new file mode 100644 index 000000000..6fb1ba96a --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_map_id2thr.c @@ -0,0 +1,63 @@ +/* Map thread ID to thread handle. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_map_id2thr (const td_thragent_t *ta, pthread_t pt, td_thrhandle_t *th) +{ +  struct pthread_handle_struct phc; +  struct _pthread_descr_struct pds; +  int pthread_threads_max; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Make the following expression a bit smaller.  */ +  pthread_threads_max = ta->pthread_threads_max; + +  /* We can compute the entry in the handle array we want.  */ +  if (ps_pdread (ta->ph, ta->handles + pt % pthread_threads_max, &phc, +		 sizeof (struct pthread_handle_struct)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Test whether this entry is in use.  */ +  if (phc.h_descr == NULL) +    return TD_BADTH; + +  /* Next test: get the descriptor to see whether this is not an old +     thread handle.  */ +  if (ps_pdread (ta->ph, phc.h_descr, &pds, +		 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  if (pds.p_tid != pt) +    return TD_BADTH; + +  /* Create the `td_thrhandle_t' object.  */ +  th->th_ta_p = (td_thragent_t *) ta; +  th->th_unique = phc.h_descr; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_map_lwp2thr.c b/libpthread/linuxthreads_db/td_ta_map_lwp2thr.c new file mode 100644 index 000000000..8c934294b --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_map_lwp2thr.c @@ -0,0 +1,81 @@ +/* Which thread is running on an lwp? +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_map_lwp2thr (const td_thragent_t *ta, lwpid_t lwpid, td_thrhandle_t *th) +{ +  int pthread_threads_max = ta->pthread_threads_max; +  size_t sizeof_descr = ta->sizeof_descr; +  struct pthread_handle_struct phc[pthread_threads_max]; +  size_t cnt; +#ifdef ALL_THREADS_STOPPED +  int num; +#else +# define num 1 +#endif + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Read all the descriptors.  */ +  if (ps_pdread (ta->ph, ta->handles, phc, +		 sizeof (struct pthread_handle_struct) * pthread_threads_max) +      != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +#ifdef ALL_THREADS_STOPPED +  /* Read the number of currently active threads.  */ +  if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ +#endif + +  /* Get the entries one after the other and find out whether the ID +     matches.  */ +  for (cnt = 0; cnt < pthread_threads_max && num > 0; ++cnt) +    if (phc[cnt].h_descr != NULL) +      { +	struct _pthread_descr_struct pds; + +#ifdef ALL_THREADS_STOPPED +	/* First count this active thread.  */ +	--num; +#endif + +	if (ps_pdread (ta->ph, phc[cnt].h_descr, &pds, sizeof_descr) != PS_OK) +	  return TD_ERR;	/* XXX Other error value?  */ + +	if ((pds.p_pid ?: ps_getpid (ta->ph)) == lwpid) +	  { +	    /* Found it.  Now fill in the `td_thrhandle_t' object.  */ +	    th->th_ta_p = (td_thragent_t *) ta; +	    th->th_unique = phc[cnt].h_descr; + +	    return TD_OK; +	  } +    } + +  return TD_NOLWP; +} diff --git a/libpthread/linuxthreads_db/td_ta_new.c b/libpthread/linuxthreads_db/td_ta_new.c new file mode 100644 index 000000000..32b9b04c9 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_new.c @@ -0,0 +1,154 @@ +/* Attach to target process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> +#include <stdlib.h> +//#include <gnu/lib-names.h> + +#include "thread_dbP.h" + + +/* Datatype for the list of known thread agents.  Normally there will +   be exactly one so we don't spend much though on making it fast.  */ +struct agent_list *__td_agent_list; + + +td_err_e +td_ta_new (struct ps_prochandle *ps, td_thragent_t **ta) +{ +  psaddr_t addr; +  struct agent_list *elemp; + +  LOG (__FUNCTION__); + +  /* Get the global event mask.  This is one of the variables which +     are new in the thread library to enable debugging.  If it is +     not available we cannot debug.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__pthread_threads_events", &addr) != PS_OK) +    return TD_NOLIBTHREAD; + +  /* Fill in the appropriate information.  */ +  *ta = (td_thragent_t *) malloc (sizeof (td_thragent_t)); +  if (*ta == NULL) +    return TD_MALLOC; + +  /* Store the proc handle which we will pass to the callback functions +     back into the debugger.  */ +  (*ta)->ph = ps; + +  /* Remember the address.  */ +  (*ta)->pthread_threads_eventsp = (td_thr_events_t *) addr; + +  /* Get the pointer to the variable pointing to the thread descriptor +     with the last event.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__pthread_last_event", +			 &(*ta)->pthread_last_event) != PS_OK) +    { +    free_return: +      free (*ta); +      return TD_ERR; +    } + +  /* Get the pointer to the variable containing the number of active +     threads.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__pthread_handles_num", +			 &(*ta)->pthread_handles_num) != PS_OK) +    goto free_return; + +  /* See whether the library contains the necessary symbols.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, "__pthread_handles", +		         &addr) != PS_OK) +    goto free_return; + +  (*ta)->handles = (struct pthread_handle_struct *) addr; + + +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, "pthread_keys", +		         &addr) != PS_OK) +    goto free_return; + +  /* Cast to the right type.  */ +  (*ta)->keys = (struct pthread_key_struct *) addr; + +  /* Find out about the maximum number of threads.  Old implementations +     don't provide this information.  In this case we assume that the +     debug  library is compiled with the same values.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__linuxthreads_pthread_threads_max", &addr) != PS_OK) +    (*ta)->pthread_threads_max = PTHREAD_THREADS_MAX; +  else +    { +      if (ps_pdread (ps, addr, &(*ta)->pthread_threads_max, sizeof (int)) +	  != PS_OK) +	goto free_return; +    } + +  /* Similar for the maximum number of thread local data keys.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__linuxthreads_pthread_keys_max", &addr) != PS_OK) +    (*ta)->pthread_keys_max = PTHREAD_KEYS_MAX; +  else +    { +      if (ps_pdread (ps, addr, &(*ta)->pthread_keys_max, sizeof (int)) +	  != PS_OK) +	goto free_return; +    } + +  /* And for the size of the second level arrays for the keys.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__linuxthreads_pthread_sizeof_descr", &addr) +      != PS_OK) +    (*ta)->sizeof_descr = sizeof (struct _pthread_descr_struct); +  else +    { +      if (ps_pdread (ps, addr, &(*ta)->sizeof_descr, sizeof (int)) != PS_OK) +	goto free_return; +    } + +  /* Similar for the maximum number of thread local data keys.  */ +  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, +			 "__linuxthreads_pthread_keys_max", &addr) != PS_OK) +    (*ta)->pthread_keys_max = PTHREAD_KEYS_MAX; +  else +    { +      if (ps_pdread (ps, addr, &(*ta)->pthread_keys_max, sizeof (int)) +	  != PS_OK) +	goto free_return; +    } + +  /* Now add the new agent descriptor to the list.  */ +  elemp = (struct agent_list *) malloc (sizeof (struct agent_list)); +  if (elemp == NULL) +    { +      /* Argh, now that everything else worked...  */ +      free (*ta); +      return TD_MALLOC; +    } + +  /* We don't care for thread-safety here.  */ +  elemp->ta = *ta; +  elemp->next = __td_agent_list; +  __td_agent_list = elemp; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_reset_stats.c b/libpthread/linuxthreads_db/td_ta_reset_stats.c new file mode 100644 index 000000000..11401b950 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_reset_stats.c @@ -0,0 +1,35 @@ +/* Reset statistics. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_reset_stats (const td_thragent_t *ta) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_set_event.c b/libpthread/linuxthreads_db/td_ta_set_event.c new file mode 100644 index 000000000..4d87fe1b5 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_set_event.c @@ -0,0 +1,53 @@ +/* Globally enable events. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_set_event (ta, event) +     const td_thragent_t *ta; +     td_thr_events_t *event; +{ +  td_thr_events_t old_event; +  int i; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdread (ta->ph, ta->pthread_threads_eventsp, +		 &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Or the new bits in.  */ +  for (i = 0; i < TD_EVENTSIZE; ++i) +    old_event.event_bits[i] |= event->event_bits[i]; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdwrite (ta->ph, ta->pthread_threads_eventsp, +		  &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_ta_setconcurrency.c b/libpthread/linuxthreads_db/td_ta_setconcurrency.c new file mode 100644 index 000000000..5bb2601b4 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_setconcurrency.c @@ -0,0 +1,35 @@ +/* Set suggested concurrency level for process. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_ta_setconcurrency (const td_thragent_t *ta, int level) +{ +  /* This is something LinuxThreads does not support.  */ +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  return TD_NOCAPAB; +} diff --git a/libpthread/linuxthreads_db/td_ta_thr_iter.c b/libpthread/linuxthreads_db/td_ta_thr_iter.c new file mode 100644 index 000000000..f1223d113 --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_thr_iter.c @@ -0,0 +1,143 @@ +/* Iterate over a process's threads. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stdlib.h> +#include "thread_dbP.h" + + +static int +handle_descr (const td_thragent_t *ta, td_thr_iter_f *callback, +	      void *cbdata_p, td_thr_state_e state, int ti_pri, +	      size_t cnt, pthread_descr descr) +{ +  struct _pthread_descr_struct pds; +  size_t sizeof_descr = ta->sizeof_descr; +  td_thrhandle_t th; + +  if (ps_pdread (ta->ph, descr, &pds, sizeof_descr) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* The manager thread must be handled special.  The descriptor +     exists but the thread only gets created when the first +     `pthread_create' call is issued.  A clear indication that this +     happened is when the p_pid field is non-zero.  */ +  if (cnt == 1 && pds.p_pid == 0) +    return TD_OK; + +  /* Now test whether this thread matches the specified +     conditions.  */ + +  /* Only if the priority level is as high or higher.  */ +  if (pds.p_priority < ti_pri) +    return TD_OK; + +  /* Test the state. +     XXX This is incomplete.  */ +  if (state != TD_THR_ANY_STATE) +    return TD_OK; + +  /* XXX For now we ignore threads which are not running anymore. +     The reason is that gdb tries to get the registers and fails. +     In future we should have a special mode of the thread library +     in which we keep the process around until the actual join +     operation happened.  */ +  if (pds.p_exited != 0) +    return TD_OK; + +  /* Yep, it matches.  Call the callback function.  */ +  th.th_ta_p = (td_thragent_t *) ta; +  th.th_unique = descr; +  if (callback (&th, cbdata_p) != 0) +    return TD_DBERR; + +  /* All done successfully.  */ +  return TD_OK; +} + + +td_err_e +td_ta_thr_iter (const td_thragent_t *ta, td_thr_iter_f *callback, +		void *cbdata_p, td_thr_state_e state, int ti_pri, +		sigset_t *ti_sigmask_p, unsigned int ti_user_flags) +{ +  int pthread_threads_max; +  struct pthread_handle_struct *phc; +  td_err_e result = TD_OK; +  int cnt; +#ifdef ALL_THREADS_STOPPED +  int num; +#else +# define num 1 +#endif + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  pthread_threads_max = ta->pthread_threads_max; +  phc = (struct pthread_handle_struct *) alloca (sizeof (phc[0]) +						 * pthread_threads_max); + +  /* First read only the main thread and manager thread information.  */ +  if (ps_pdread (ta->ph, ta->handles, phc, +		 sizeof (struct pthread_handle_struct) * 2) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Now handle these descriptors.  */ +  result = handle_descr (ta, callback, cbdata_p, state, ti_pri, 0, +			 phc[0].h_descr); +  if (result != TD_OK) +    return result; +  result = handle_descr (ta, callback, cbdata_p, state, ti_pri, 1, +			 phc[1].h_descr); +  if (result != TD_OK) +    return result; + +  /* Read all the descriptors.  */ +  if (ps_pdread (ta->ph, ta->handles + 2, &phc[2], +		 (sizeof (struct pthread_handle_struct) +		  * (pthread_threads_max - 2))) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +#ifdef ALL_THREADS_STOPPED +  /* Read the number of currently active threads.  */ +  if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ +#endif + +  /* Now get all descriptors, one after the other.  */ +  for (cnt = 2; cnt < pthread_threads_max && num > 0; ++cnt) +    if (phc[cnt].h_descr != NULL) +      { +#ifdef ALL_THREADS_STOPPED +	/* First count this active thread.  */ +	--num; +#endif + +	result = handle_descr (ta, callback, cbdata_p, state, ti_pri, cnt, +			       phc[cnt].h_descr); +	if (result != TD_OK) +	  break; +      } + +  return result; +} diff --git a/libpthread/linuxthreads_db/td_ta_tsd_iter.c b/libpthread/linuxthreads_db/td_ta_tsd_iter.c new file mode 100644 index 000000000..b761be1df --- /dev/null +++ b/libpthread/linuxthreads_db/td_ta_tsd_iter.c @@ -0,0 +1,56 @@ +/* Iterate over a process's thread-specific data. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stdlib.h> +#include "thread_dbP.h" + + +td_err_e +td_ta_tsd_iter (const td_thragent_t *ta, td_key_iter_f *callback, +		void *cbdata_p) +{ +  struct pthread_key_struct *keys; +  int pthread_keys_max; +  int cnt; + +  LOG (__FUNCTION__); + +  /* Test whether the TA parameter is ok.  */ +  if (! ta_ok (ta)) +    return TD_BADTA; + +  pthread_keys_max = ta->pthread_keys_max; +  keys = (struct pthread_key_struct *) alloca (sizeof (keys[0]) +					       * pthread_keys_max); + +  /* Read all the information about the keys.  */ +  if (ps_pdread (ta->ph, ta->keys, keys, +		 sizeof (keys[0]) * pthread_keys_max) != PS_OK) +	return TD_ERR;	/* XXX Other error value?  */ + +  /* Now get all descriptors, one after the other.  */ +  for (cnt = 0; cnt < pthread_keys_max; ++cnt) +    if (keys[cnt].in_use +	/* Return with an error if the callback returns a nonzero value.  */ +	&& callback (cnt, keys[cnt].destr, cbdata_p) != 0) +      return TD_DBERR; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_clear_event.c b/libpthread/linuxthreads_db/td_thr_clear_event.c new file mode 100644 index 000000000..8ef8cbbc4 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_clear_event.c @@ -0,0 +1,57 @@ +/* Disable specific event for thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> + +#include "thread_dbP.h" + + +td_err_e +td_thr_clear_event (th, event) +     const td_thrhandle_t *th; +     td_thr_events_t *event; +{ +  td_thr_events_t old_event; +  int i; + +  LOG (__FUNCTION__); + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdread (th->th_ta_p->ph, +		 ((char *) th->th_unique +		  + offsetof (struct _pthread_descr_struct, +			      p_eventbuf.eventmask)), +		 &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Remove the set bits in.  */ +  for (i = 0; i < TD_EVENTSIZE; ++i) +    old_event.event_bits[i] &= ~event->event_bits[i]; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdwrite (th->th_ta_p->ph, +		  ((char *) th->th_unique +		   + offsetof (struct _pthread_descr_struct, +			       p_eventbuf.eventmask)), +		  &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_dbresume.c b/libpthread/linuxthreads_db/td_thr_dbresume.c new file mode 100644 index 000000000..692943a74 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_dbresume.c @@ -0,0 +1,30 @@ +/* Resume execution of given thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_dbresume (const td_thrhandle_t *th) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_NOCAPAB; +} diff --git a/libpthread/linuxthreads_db/td_thr_dbsuspend.c b/libpthread/linuxthreads_db/td_thr_dbsuspend.c new file mode 100644 index 000000000..9e7ceb653 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_dbsuspend.c @@ -0,0 +1,30 @@ +/* Suspend execution of given thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_dbsuspend (const td_thrhandle_t *th) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_NOCAPAB; +} diff --git a/libpthread/linuxthreads_db/td_thr_event_enable.c b/libpthread/linuxthreads_db/td_thr_event_enable.c new file mode 100644 index 000000000..79e7d3a2a --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_event_enable.c @@ -0,0 +1,41 @@ +/* Enable event process-wide. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> + +#include "thread_dbP.h" + + +td_err_e +td_thr_event_enable (th, onoff) +     const td_thrhandle_t *th; +     int onoff; +{ +  LOG (__FUNCTION__); + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdwrite (th->th_ta_p->ph, +		  ((char *) th->th_unique +		   + offsetof (struct _pthread_descr_struct, p_report_events)), +		  &onoff, sizeof (int)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_event_getmsg.c b/libpthread/linuxthreads_db/td_thr_event_getmsg.c new file mode 100644 index 000000000..4812ece4d --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_event_getmsg.c @@ -0,0 +1,60 @@ +/* Retrieve event. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> +#include <string.h> + +#include "thread_dbP.h" + + +td_err_e +td_thr_event_getmsg (const td_thrhandle_t *th, td_event_msg_t *msg) +{ +  td_eventbuf_t event; + +  LOG (__FUNCTION__); + +  /* Read the even structure from the target.  */ +  if (ps_pdread (th->th_ta_p->ph, +		 ((char *) th->th_unique +		  + offsetof (struct _pthread_descr_struct, p_eventbuf)), +		 &event, sizeof (td_eventbuf_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Check whether an event occurred.  */ +  if (event.eventnum == TD_EVENT_NONE) +    /* Nothing.  */ +    return TD_NOMSG; + +  /* Fill the user's data structure.  */ +  msg->event = event.eventnum; +  msg->th_p = th; +  msg->msg.data = (uintptr_t) event.eventdata; + +  /* And clear the event message in the target.  */ +  memset (&event, '\0', sizeof (td_eventbuf_t)); +  if (ps_pdwrite (th->th_ta_p->ph, +		  ((char *) th->th_unique +		   + offsetof (struct _pthread_descr_struct, p_eventbuf)), +		  &event, sizeof (td_eventbuf_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_get_info.c b/libpthread/linuxthreads_db/td_thr_get_info.c new file mode 100644 index 000000000..25ad3408a --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_get_info.c @@ -0,0 +1,75 @@ +/* Get thread information. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> +#include <string.h> + +#include "thread_dbP.h" + + +td_err_e +td_thr_get_info (const td_thrhandle_t *th, td_thrinfo_t *infop) +{ +  struct _pthread_descr_struct pds; + +  LOG (__FUNCTION__); + +  /* Get the thread descriptor.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +		 th->th_ta_p->sizeof_descr) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Fill in information.  Clear first to provide reproducable +     results for the fields we do not fill in.  */ +  memset (infop, '\0', sizeof (td_thrinfo_t)); + +  /* We have to handle the manager thread special since the thread +     descriptor in older versions is not fully initialized.  */ +  if (pds.p_nr == 1) +    { +      infop->ti_tid = th->th_ta_p->pthread_threads_max * 2 + 1; +      infop->ti_type = TD_THR_SYSTEM; +      infop->ti_state = TD_THR_RUN; +    } +  else +    { +      infop->ti_tid = pds.p_tid; +      infop->ti_tls = (char *) pds.p_specific; +      infop->ti_pri = pds.p_priority; +      infop->ti_type = TD_THR_USER; + +      if (pds.p_exited) +	/* This should not happen.  */ +	infop->ti_state = TD_THR_ZOMBIE; +      else +	/* XXX For now there is no way to get more information.  */ +	infop->ti_state = TD_THR_RUN; +    } + +  /* Initialization which are the same in both cases.  */ +  infop->ti_lid = pds.p_pid ?: ps_getpid (th->th_ta_p->ph); +  infop->ti_ta_p = th->th_ta_p; +  infop->ti_startfunc = pds.p_start_args.start_routine; +  memcpy (&infop->ti_events, &pds.p_eventbuf.eventmask, +	  sizeof (td_thr_events_t)); +  infop->ti_traceme = pds.p_report_events != 0; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_getfpregs.c b/libpthread/linuxthreads_db/td_thr_getfpregs.c new file mode 100644 index 000000000..e6635d264 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_getfpregs.c @@ -0,0 +1,44 @@ +/* Get a thread's floating-point register set. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_getfpregs (const td_thrhandle_t *th, prfpregset_t *regset) +{ +  struct _pthread_descr_struct pds; + +  LOG (__FUNCTION__); + +  /* We have to get the state and the PID for this thread.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +		 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR; + +  /* If the thread already terminated we return all zeroes.  */ +  if (pds.p_terminated) +    memset (regset, '\0', sizeof (*regset)); +  /* Otherwise get the register content through the callback.  */ +  else if (ps_lgetfpregs (th->th_ta_p->ph, pds.p_pid, regset) != PS_OK) +    return TD_ERR; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_getgregs.c b/libpthread/linuxthreads_db/td_thr_getgregs.c new file mode 100644 index 000000000..a4d861970 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_getgregs.c @@ -0,0 +1,44 @@ +/* Get a thread's general register set. +   Copyright (C) 1999, 2000 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_getgregs (const td_thrhandle_t *th, prgregset_t gregs) +{ +  struct _pthread_descr_struct pds; + +  LOG (__FUNCTION__); + +  /* We have to get the state and the PID for this thread.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +		 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR; + +  /* If the thread already terminated we return all zeroes.  */ +  if (pds.p_terminated) +    memset (gregs, '\0', sizeof (prgregset_t)); +  /* Otherwise get the register content through the callback.  */ +  else if (ps_lgetregs (th->th_ta_p->ph, pds.p_pid, gregs) != PS_OK) +    return TD_ERR; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_getxregs.c b/libpthread/linuxthreads_db/td_thr_getxregs.c new file mode 100644 index 000000000..d8d568351 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_getxregs.c @@ -0,0 +1,30 @@ +/* Get a thread's extra state register set. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_getxregs (const td_thrhandle_t *th, void *xregs) +{ +  /* XXX This might be platform specific.  */ +  LOG (__FUNCTION__); +  return TD_NOXREGS; +} diff --git a/libpthread/linuxthreads_db/td_thr_getxregsize.c b/libpthread/linuxthreads_db/td_thr_getxregsize.c new file mode 100644 index 000000000..f704a31e8 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_getxregsize.c @@ -0,0 +1,30 @@ +/* Get the size of the extra state register set for this architecture. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_getxregsize (const td_thrhandle_t *th, int *sizep) +{ +  /* XXX This might be platform specific.  */ +  LOG (__FUNCTION__); +  return TD_NOXREGS; +} diff --git a/libpthread/linuxthreads_db/td_thr_set_event.c b/libpthread/linuxthreads_db/td_thr_set_event.c new file mode 100644 index 000000000..583a2f804 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_set_event.c @@ -0,0 +1,57 @@ +/* Enable specific event for thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <stddef.h> + +#include "thread_dbP.h" + + +td_err_e +td_thr_set_event (th, event) +     const td_thrhandle_t *th; +     td_thr_events_t *event; +{ +  td_thr_events_t old_event; +  int i; + +  LOG (__FUNCTION__); + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdread (th->th_ta_p->ph, +		 ((char *) th->th_unique +		  + offsetof (struct _pthread_descr_struct, +			      p_eventbuf.eventmask)), +		 &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Or the new bits in.  */ +  for (i = 0; i < TD_EVENTSIZE; ++i) +    old_event.event_bits[i] |= event->event_bits[i]; + +  /* Write the new value into the thread data structure.  */ +  if (ps_pdwrite (th->th_ta_p->ph, +		  ((char *) th->th_unique +		   + offsetof (struct _pthread_descr_struct, +			       p_eventbuf.eventmask)), +		  &old_event, sizeof (td_thrhandle_t)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_setfpregs.c b/libpthread/linuxthreads_db/td_thr_setfpregs.c new file mode 100644 index 000000000..0c426b3d3 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_setfpregs.c @@ -0,0 +1,42 @@ +/* Set a thread's floating-point register set. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_setfpregs (const td_thrhandle_t *th, const prfpregset_t *fpregs) +{ +  struct _pthread_descr_struct pds; + +  LOG (__FUNCTION__); + +  /* We have to get the state and the PID for this thread.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +                 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR; + +  /* Only set the registers if the thread hasn't yet terminated.  */ +  if (pds.p_terminated == 0 +      && ps_lsetfpregs (th->th_ta_p->ph, pds.p_pid, fpregs) != PS_OK) +    return TD_ERR; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_setgregs.c b/libpthread/linuxthreads_db/td_thr_setgregs.c new file mode 100644 index 000000000..72f95b1ca --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_setgregs.c @@ -0,0 +1,42 @@ +/* Set a thread's general register set. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_setgregs (const td_thrhandle_t *th, prgregset_t gregs) +{ +  struct _pthread_descr_struct pds; + +  LOG (__FUNCTION__); + +  /* We have to get the state and the PID for this thread.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +                 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR; + +  /* Only set the registers if the thread hasn't yet terminated.  */ +  if (pds.p_terminated == 0 +      && ps_lsetregs (th->th_ta_p->ph, pds.p_pid, gregs) != PS_OK) +    return TD_ERR; + +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_setprio.c b/libpthread/linuxthreads_db/td_thr_setprio.c new file mode 100644 index 000000000..03a503d01 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_setprio.c @@ -0,0 +1,30 @@ +/* Set a thread's priority. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_setprio (const td_thrhandle_t *th, int prio) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_setsigpending.c b/libpthread/linuxthreads_db/td_thr_setsigpending.c new file mode 100644 index 000000000..815cd2226 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_setsigpending.c @@ -0,0 +1,31 @@ +/* Raise a signal for a thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_setsigpending (const td_thrhandle_t *th, unsigned char n, +		      const sigset_t *ss) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_setxregs.c b/libpthread/linuxthreads_db/td_thr_setxregs.c new file mode 100644 index 000000000..4b4fc3475 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_setxregs.c @@ -0,0 +1,30 @@ +/* Set a thread's extra state register set. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_setxregs (const td_thrhandle_t *ta, const void *addr) +{ +  /* XXX This might have to be platform specific.  */ +  LOG (__FUNCTION__); +  return TD_NOXREGS; +} diff --git a/libpthread/linuxthreads_db/td_thr_sigsetmask.c b/libpthread/linuxthreads_db/td_thr_sigsetmask.c new file mode 100644 index 000000000..88115976c --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_sigsetmask.c @@ -0,0 +1,30 @@ +/* Set a thread's signal mask. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_sigsetmask (const td_thrhandle_t *th, const sigset_t *ss) +{ +  /* XXX We have to figure out what has to be done.  */ +  LOG (__FUNCTION__); +  return TD_OK; +} diff --git a/libpthread/linuxthreads_db/td_thr_tsd.c b/libpthread/linuxthreads_db/td_thr_tsd.c new file mode 100644 index 000000000..0453c981e --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_tsd.c @@ -0,0 +1,76 @@ +/* Get a thread-specific data pointer for a thread. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_tsd (const td_thrhandle_t *th, const thread_key_t tk, void **data) +{ +  struct _pthread_descr_struct pds; +  struct pthread_key_struct *keys = th->th_ta_p->keys; +  struct pthread_key_struct key; +  int pthread_keys_max = th->th_ta_p->pthread_keys_max; +  int pthread_key_2ndlevel_size = th->th_ta_p->pthread_key_2ndlevel_size; +  unsigned int idx1st; +  unsigned int idx2nd; +  void *p; + +  LOG (__FUNCTION__); + +  /* Get the thread descriptor.  */ +  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds, +		 sizeof (struct _pthread_descr_struct)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Check correct value of key.  */ +  if (tk >= pthread_keys_max) +    return TD_BADKEY; + +  /* Get the key entry.  */ +  if (ps_pdread (th->th_ta_p->ph, keys, &key, +		 sizeof (struct pthread_key_struct)) != PS_OK) +    return TD_ERR;	/* XXX Other error value?  */ + +  /* Fail if this key is not at all used.  */ +  if (! key.in_use) +    return TD_BADKEY; + +  /* Compute the indeces.  */ +  idx1st = tk / pthread_key_2ndlevel_size; +  idx2nd = tk % pthread_key_2ndlevel_size; + +  /* Check the pointer to the second level array.  */ +  if (pds.p_specific[idx1st] == NULL) +    return TD_NOTSD; + +  /* Now get the real key. +     XXX I don't know whether it's correct but there is currently no +     easy way to determine whether a key was never set or the value +     is NULL.  We return an error whenever the value is NULL.  */ +  if (ps_pdread (th->th_ta_p->ph, &pds.p_specific[idx1st][idx2nd], &p, +		 sizeof (void *)) != PS_OK) +    return TD_ERR; + +  if (p != NULL) +    *data = p; + +  return p != NULL ? TD_OK : TD_NOTSD; +} diff --git a/libpthread/linuxthreads_db/td_thr_validate.c b/libpthread/linuxthreads_db/td_thr_validate.c new file mode 100644 index 000000000..81c3b5021 --- /dev/null +++ b/libpthread/linuxthreads_db/td_thr_validate.c @@ -0,0 +1,51 @@ +/* Validate a thread handle. +   Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include "thread_dbP.h" + + +td_err_e +td_thr_validate (const td_thrhandle_t *th) +{ +  struct pthread_handle_struct *handles = th->th_ta_p->handles; +  int pthread_threads_max = th->th_ta_p->pthread_threads_max; +  int cnt; + +  LOG (__FUNCTION__); + +  /* Now get all descriptors, one after the other.  */ +  for (cnt = 0; cnt < pthread_threads_max; ++cnt, ++handles) +    { +      struct pthread_handle_struct phc; + +      if (ps_pdread (th->th_ta_p->ph, handles, &phc, +		     sizeof (struct pthread_handle_struct)) != PS_OK) +	return TD_ERR;	/* XXX Other error value?  */ + +      if (phc.h_descr != NULL && phc.h_descr == th->th_unique) +	{ +	  /* XXX There should be another test using the TID but this is +	     currently not available.  */ +	  return TD_OK; +	} +    } + +  return TD_ERR; +} diff --git a/libpthread/linuxthreads_db/thread_db.h b/libpthread/linuxthreads_db/thread_db.h new file mode 100644 index 000000000..6301d7fa5 --- /dev/null +++ b/libpthread/linuxthreads_db/thread_db.h @@ -0,0 +1,436 @@ +/* Copyright (C) 1999 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Library General Public License as +   published by the Free Software Foundation; either version 2 of the +   License, or (at your option) any later version. + +   The GNU C Library 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 +   Library General Public License for more details. + +   You should have received a copy of the GNU Library General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#ifndef _THREAD_DB_H +#define _THREAD_DB_H	1 + +/* This is the debugger interface for the LinuxThreads library.  It is +   modelled closely after the interface with same names in Solaris with +   the goal to share the same code in the debugger.  */ +#include <pthread.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/procfs.h> + + +/* Error codes of the library.  */ +typedef enum +{ +  TD_OK,	  /* No error.  */ +  TD_ERR,	  /* No further specified error.  */ +  TD_NOTHR,	  /* No matching thread found.  */ +  TD_NOSV,	  /* No matching synchronization handle found.  */ +  TD_NOLWP,	  /* No matching light-weighted process found.  */ +  TD_BADPH,	  /* Invalid process handle.  */ +  TD_BADTH,	  /* Invalid thread handle.  */ +  TD_BADSH,	  /* Invalid synchronization handle.  */ +  TD_BADTA,	  /* Invalid thread agent.  */ +  TD_BADKEY,	  /* Invalid key.  */ +  TD_NOMSG,	  /* No event available.  */ +  TD_NOFPREGS,	  /* No floating-point register content available.  */ +  TD_NOLIBTHREAD, /* Application not linked with thread library.  */ +  TD_NOEVENT,	  /* Requested event is not supported.  */ +  TD_NOCAPAB,	  /* Capability not available.  */ +  TD_DBERR,	  /* Internal debug library error.  */ +  TD_NOAPLIC,	  /* Operation is not applicable.  */ +  TD_NOTSD,	  /* No thread-specific data available.  */ +  TD_MALLOC,	  /* Out of memory.  */ +  TD_PARTIALREG,  /* Not entire register set was read or written.  */ +  TD_NOXREGS	  /* X register set not available for given thread.  */ +} td_err_e; + + +/* Possible thread states.  TD_THR_ANY_STATE is a pseudo-state used to +   select threads regardless of state in td_ta_thr_iter().  */ +typedef enum +{ +  TD_THR_ANY_STATE, +  TD_THR_UNKNOWN, +  TD_THR_STOPPED, +  TD_THR_RUN, +  TD_THR_ACTIVE, +  TD_THR_ZOMBIE, +  TD_THR_SLEEP, +  TD_THR_STOPPED_ASLEEP +} td_thr_state_e; + +/* Thread type: user or system.  TD_THR_ANY_TYPE is a pseudo-type used +   to select threads regardless of type in td_ta_thr_iter().  */ +typedef enum +{ +  TD_THR_ANY_TYPE, +  TD_THR_USER, +  TD_THR_SYSTEM +} td_thr_type_e; + + +/* Types of the debugging library.  */ + +/* Handle for a process.  This type is opaque.  */ +typedef struct td_thragent td_thragent_t; + +/* The actual thread handle type.  This is also opaque.  */ +typedef struct td_thrhandle +{ +  td_thragent_t *th_ta_p; +  psaddr_t th_unique; +} td_thrhandle_t; + + +/* Flags for `td_ta_thr_iter'.  */ +#define TD_THR_ANY_USER_FLAGS	0xffffffff +#define TD_THR_LOWEST_PRIORITY	-20 +#define TD_SIGNO_MASK		NULL + + +#define TD_EVENTSIZE	2 +#define BT_UISHIFT	5 /* log base 2 of BT_NBIPUI, to extract word index */ +#define BT_NBIPUI	(1 << BT_UISHIFT)       /* n bits per uint */ +#define BT_UIMASK	(BT_NBIPUI - 1)         /* to extract bit index */ + +/* Bitmask of enabled events. */ +typedef struct td_thr_events +{ +  uint32_t event_bits[TD_EVENTSIZE]; +} td_thr_events_t; + +/* Event set manipulation macros. */ +#define __td_eventmask(n) \ +  (UINT32_C (1) << (((n) - 1) & BT_UIMASK)) +#define __td_eventword(n) \ +  ((UINT32_C ((n) - 1)) >> BT_UISHIFT) + +#define td_event_emptyset(setp) \ +  do {									      \ +    int __i;								      \ +    for (__i = TD_EVENTSIZE; __i > 0; --__i)				      \ +      (setp)->event_bits[__i - 1] = 0;					      \ +  } while (0) + +#define td_event_fillset(setp) \ +  do {									      \ +    int __i;								      \ +    for (__i = TD_EVENTSIZE; __i > 0; --__i)				      \ +      (setp)->event_bits[__i - 1] = UINT32_C (0xffffffff);		      \ +  } while (0) + +#define td_event_addset(setp, n) \ +  (((setp)->event_bits[__td_eventword (n)]) |= __td_eventmask (n)) +#define td_event_delset(setp, n) \ +  (((setp)->event_bits[__td_eventword (n)]) &= ~__td_eventmask (n)) +#define td_eventismember(setp, n) \ +  (__td_eventmask (n) & ((setp)->event_bits[__td_eventword (n)])) +#if TD_EVENTSIZE == 2 +# define td_eventisempty(setp) \ +  (!((setp)->event_bits[0]) && !((setp)->event_bits[1])) +#else +# error "td_eventisempty must be changed to match TD_EVENTSIZE" +#endif + +/* Events reportable by the thread implementation.  */ +typedef enum +{ +  TD_ALL_EVENTS,		 /* Pseudo-event number.  */ +  TD_EVENT_NONE = TD_ALL_EVENTS, /* Depends on context.  */ +  TD_READY,			 /* Is executable now. */ +  TD_SLEEP,			 /* Blocked in a synchronization obj.  */ +  TD_SWITCHTO,			 /* Now assigned to a process.  */ +  TD_SWITCHFROM,		 /* Not anymore assigned to a process.  */ +  TD_LOCK_TRY,			 /* Trying to get an unavailable lock.  */ +  TD_CATCHSIG,			 /* Signal posted to the thread.  */ +  TD_IDLE,			 /* Process getting idle.  */ +  TD_CREATE,			 /* New thread created.  */ +  TD_DEATH,			 /* Thread terminated.  */ +  TD_PREEMPT,			 /* Preempted.  */ +  TD_PRI_INHERIT,		 /* Inherited elevated priority.  */ +  TD_REAP,			 /* Reaped.  */ +  TD_CONCURRENCY,		 /* Number of processes changing.  */ +  TD_TIMEOUT,			 /* Conditional variable wait timed out.  */ +  TD_MIN_EVENT_NUM = TD_READY, +  TD_MAX_EVENT_NUM = TD_TIMEOUT, +  TD_EVENTS_ENABLE = 31		/* Event reporting enabled.  */ +} td_event_e; + +/* Values representing the different ways events are reported.  */ +typedef enum +{ +  NOTIFY_BPT,			/* User must insert breakpoint at u.bptaddr. */ +  NOTIFY_AUTOBPT,		/* Breakpoint at u.bptaddr is automatically +				   inserted.  */ +  NOTIFY_SYSCALL		/* System call u.syscallno will be invoked.  */ +} td_notify_e; + +/* Description how event type is reported.  */ +typedef struct td_notify +{ +  td_notify_e type;		/* Way the event is reported.  */ +  union +  { +    psaddr_t bptaddr;		/* Address of breakpoint.  */ +    int syscallno;		/* Number of system call used.  */ +  } u; +} td_notify_t; + +/* Structure used to report event.  */ +typedef struct td_event_msg +{ +  td_event_e event;		/* Event type being reported.  */ +  const td_thrhandle_t *th_p;	/* Thread reporting the event.  */ +  union +  { +# if 0 +    td_synchandle_t *sh;	/* Handle of synchronization object.  */ +#endif +    uintptr_t data;		/* Event specific data.  */ +  } msg; +} td_event_msg_t; + +/* Structure containing event data available in each thread structure.  */ +typedef struct +{ +  td_thr_events_t eventmask;	/* Mask of enabled events.  */ +  td_event_e eventnum;		/* Number of last event.  */ +  void *eventdata;		/* Data associated with event.  */ +} td_eventbuf_t; + + +/* Gathered statistics about the process.  */ +typedef struct td_ta_stats +{ +  int nthreads;       		/* Total number of threads in use.  */ +  int r_concurrency;		/* Concurrency level requested by user.  */ +  int nrunnable_num;		/* Average runnable threads, numerator.  */ +  int nrunnable_den;		/* Average runnable threads, denominator.  */ +  int a_concurrency_num;	/* Achieved concurrency level, numerator.  */ +  int a_concurrency_den;	/* Achieved concurrency level, denominator.  */ +  int nlwps_num;		/* Average number of processes in use, +				   numerator.  */ +  int nlwps_den;		/* Average number of processes in use, +				   denominator.  */ +  int nidle_num;		/* Average number of idling processes, +				   numerator.  */ +  int nidle_den;		/* Average number of idling processes, +				   denominator.  */ +} td_ta_stats_t; + + +/* Since Sun's library is based on Solaris threads we have to define a few +   types to map them to POSIX threads.  */ +typedef pthread_t thread_t; +typedef pthread_key_t thread_key_t; + + +/* Callback for iteration over threads.  */ +typedef int td_thr_iter_f (const td_thrhandle_t *, void *); + +/* Callback for iteration over thread local data.  */ +typedef int td_key_iter_f (thread_key_t, void (*) (void *), void *); + + + +/* Forward declaration.  This has to be defined by the user.  */ +struct ps_prochandle; + + +/* Information about the thread.  */ +typedef struct td_thrinfo +{ +  td_thragent_t *ti_ta_p;		/* Process handle.  */ +  unsigned int ti_user_flags;		/* Unused.  */ +  thread_t ti_tid;			/* Thread ID returned by +					   pthread_create().  */ +  char *ti_tls;				/* Pointer to thread-local data.  */ +  psaddr_t ti_startfunc;		/* Start function passed to +					   pthread_create().  */ +  psaddr_t ti_stkbase;			/* Base of thread's stack.  */ +  long int ti_stksize;			/* Size of thread's stack.  */ +  psaddr_t ti_ro_area;			/* Unused.  */ +  int ti_ro_size;			/* Unused.  */ +  td_thr_state_e ti_state;		/* Thread state.  */ +  unsigned char ti_db_suspended;	/* Nonzero if suspended by debugger. */ +  td_thr_type_e ti_type;		/* Type of the thread (system vs +					   user thread).  */ +  intptr_t ti_pc;			/* Unused.  */ +  intptr_t ti_sp;			/* Unused.  */ +  short int ti_flags;			/* Unused.  */ +  int ti_pri;				/* Thread priority.  */ +  lwpid_t ti_lid;			/* Unused.  */ +  sigset_t ti_sigmask;			/* Signal mask.  */ +  unsigned char ti_traceme;		/* Nonzero if event reporting +					   enabled.  */ +  unsigned char ti_preemptflag;		/* Unused.  */ +  unsigned char ti_pirecflag;		/* Unused.  */ +  sigset_t ti_pending;			/* Set of pending signals.  */ +  td_thr_events_t ti_events;		/* Set of enabled events.  */ +} td_thrinfo_t; + + + +/* Prototypes for exported library functions.  */ + +/* Initialize the thread debug support library.  */ +extern td_err_e td_init (void); + +/* Historical relict.  Should not be used anymore.  */ +extern td_err_e td_log (void); + +/* Generate new thread debug library handle for process PS.  */ +extern td_err_e td_ta_new (struct ps_prochandle *__ps, td_thragent_t **__ta); + +/* Free resources allocated for TA.  */ +extern td_err_e td_ta_delete (td_thragent_t *__ta); + +/* Get number of currently running threads in process associated with TA.  */ +extern td_err_e td_ta_get_nthreads (const td_thragent_t *__ta, int *__np); + +/* Return process handle passed in `td_ta_new' for process associated with +   TA.  */ +extern td_err_e td_ta_get_ph (const td_thragent_t *__ta, +			      struct ps_prochandle **__ph); + +/* Map thread library handle PT to thread debug library handle for process +   associated with TA and store result in *TH.  */ +extern td_err_e td_ta_map_id2thr (const td_thragent_t *__ta, pthread_t __pt, +				  td_thrhandle_t *__th); + +/* Map process ID LWPID to thread debug library handle for process +   associated with TA and store result in *TH.  */ +extern td_err_e td_ta_map_lwp2thr (const td_thragent_t *__ta, lwpid_t __lwpid, +				   td_thrhandle_t *__th); + + +/* Call for each thread in a process associated with TA the callback function +   CALLBACK.  */ +extern td_err_e td_ta_thr_iter (const td_thragent_t *__ta, +				td_thr_iter_f *__callback, void *__cbdata_p, +				td_thr_state_e __state, int __ti_pri, +				sigset_t *__ti_sigmask_p, +				unsigned int __ti_user_flags); + +/* Call for each defined thread local data entry the callback function KI.  */ +extern td_err_e td_ta_tsd_iter (const td_thragent_t *__ta, td_key_iter_f *__ki, +				void *__p); + + +/* Get event address for EVENT.  */ +extern td_err_e td_ta_event_addr (const td_thragent_t *__ta, +				  td_event_e __event, td_notify_t *__ptr); + +/* Enable EVENT in global mask.  */ +extern td_err_e td_ta_set_event (const td_thragent_t *__ta, +				 td_thr_events_t *__event); + +/* Disable EVENT in global mask.  */ +extern td_err_e td_ta_clear_event (const td_thragent_t *__ta, +				   td_thr_events_t *__event); + +/* Return information about last event.  */ +extern td_err_e td_ta_event_getmsg (const td_thragent_t *__ta, +				    td_event_msg_t *msg); + + +/* Set suggested concurrency level for process associated with TA.  */ +extern td_err_e td_ta_setconcurrency (const td_thragent_t *__ta, int __level); + + +/* Enable collecting statistics for process associated with TA.  */ +extern td_err_e td_ta_enable_stats (const td_thragent_t *__ta, int __enable); + +/* Reset statistics.  */ +extern td_err_e td_ta_reset_stats (const td_thragent_t *__ta); + +/* Retrieve statistics from process associated with TA.  */ +extern td_err_e td_ta_get_stats (const td_thragent_t *__ta, +				 td_ta_stats_t *__statsp); + + +/* Validate that TH is a thread handle.  */ +extern td_err_e td_thr_validate (const td_thrhandle_t *__th); + +/* Return information about thread TH.  */ +extern td_err_e td_thr_get_info (const td_thrhandle_t *__th, +				 td_thrinfo_t *__infop); + +/* Retrieve floating-point register contents of process running thread TH.  */ +extern td_err_e td_thr_getfpregs (const td_thrhandle_t *__th, +				  prfpregset_t *__regset); + +/* Retrieve general register contents of process running thread TH.  */ +extern td_err_e td_thr_getgregs (const td_thrhandle_t *__th, +				 prgregset_t __gregs); + +/* Retrieve extended register contents of process running thread TH.  */ +extern td_err_e td_thr_getxregs (const td_thrhandle_t *__th, void *__xregs); + +/* Get size of extended register set of process running thread TH.  */ +extern td_err_e td_thr_getxregsize (const td_thrhandle_t *__th, int *__sizep); + +/* Set floating-point register contents of process running thread TH.  */ +extern td_err_e td_thr_setfpregs (const td_thrhandle_t *__th, +				  const prfpregset_t *__fpregs); + +/* Set general register contents of process running thread TH.  */ +extern td_err_e td_thr_setgregs (const td_thrhandle_t *__th, +				 prgregset_t __gregs); + +/* Set extended register contents of process running thread TH.  */ +extern td_err_e td_thr_setxregs (const td_thrhandle_t *__th, +				 const void *__addr); + + +/* Enable reporting for EVENT for thread TH.  */ +extern td_err_e td_thr_event_enable (const td_thrhandle_t *__th, int __event); + +/* Enable EVENT for thread TH.  */ +extern td_err_e td_thr_set_event (const td_thrhandle_t *__th, +				  td_thr_events_t *__event); + +/* Disable EVENT for thread TH.  */ +extern td_err_e td_thr_clear_event (const td_thrhandle_t *__th, +				    td_thr_events_t *__event); + +/* Get event message for thread TH.  */ +extern td_err_e td_thr_event_getmsg (const td_thrhandle_t *__th, +				     td_event_msg_t *__msg); + + +/* Set priority of thread TH.  */ +extern td_err_e td_thr_setprio (const td_thrhandle_t *__th, int __prio); + + +/* Set pending signals for thread TH.  */ +extern td_err_e td_thr_setsigpending (const td_thrhandle_t *__th, +				      unsigned char __n, const sigset_t *__ss); + +/* Set signal mask for thread TH.  */ +extern td_err_e td_thr_sigsetmask (const td_thrhandle_t *__th, +				   const sigset_t *__ss); + + +/* Return thread local data associated with key TK in thread TH.  */ +extern td_err_e td_thr_tsd (const td_thrhandle_t *__th, +			    const thread_key_t __tk, void **__data); + + +/* Suspend execution of thread TH.  */ +extern td_err_e td_thr_dbsuspend (const td_thrhandle_t *__th); + +/* Resume execution of thread TH.  */ +extern td_err_e td_thr_dbresume (const td_thrhandle_t *__th); + +#endif	/* thread_db.h */ diff --git a/libpthread/linuxthreads_db/thread_dbP.h b/libpthread/linuxthreads_db/thread_dbP.h new file mode 100644 index 000000000..13e534afe --- /dev/null +++ b/libpthread/linuxthreads_db/thread_dbP.h @@ -0,0 +1,83 @@ +/* Private header for thread debug library.  */ +#ifndef _THREAD_DBP_H +#define _THREAD_DBP_H	1 + +#include <string.h> +#include "proc_service.h" +#include "thread_db.h" +#include "../linuxthreads/internals.h" + + +/* Comment out the following for less verbose output.  */ +#ifndef NDEBUG +# define LOG(c) if (__td_debug) __libc_write (2, c "\n", strlen (c "\n")) +extern int __td_debug; +#else +# define LOG(c) +#endif + + +/* Handle for a process.  This type is opaque.  */ +struct td_thragent +{ +  /* Delivered by the debugger and we have to pass it back in the +     proc callbacks.  */ +  struct ps_prochandle *ph; + +  /* Some cached information.  */ + +  /* Address of the `__pthread_handles' array.  */ +  struct pthread_handle_struct *handles; + +  /* Address of the `pthread_kyes' array.  */ +  struct pthread_key_struct *keys; + +  /* Maximum number of threads.  */ +  int pthread_threads_max; + +  /* Maximum number of thread-local data keys.  */ +  int pthread_keys_max; + +  /* Size of 2nd level array for thread-local data keys.  */ +  int pthread_key_2ndlevel_size; + +  /* Sizeof struct _pthread_descr_struct.  */ +  int sizeof_descr; + +  /* Pointer to the `__pthread_threads_events' variable in the target.  */ +  psaddr_t pthread_threads_eventsp; + +  /* Pointer to the `__pthread_last_event' variable in the target.  */ +  psaddr_t pthread_last_event; + +  /* Pointer to the `__pthread_handles_num' variable.  */ +  psaddr_t pthread_handles_num; +}; + + +/* Type used internally to keep track of thread agent descriptors.  */ +struct agent_list +{ +  td_thragent_t *ta; +  struct agent_list *next; +}; + +/* List of all known descriptors.  */ +extern struct agent_list *__td_agent_list; + +/* Function used to test for correct thread agent pointer.  */ +static inline int +ta_ok (const td_thragent_t *ta) +{ +  struct agent_list *runp = __td_agent_list; + +  if (ta == NULL) +    return 0; + +  while (runp != NULL && runp->ta != ta) +    runp = runp->next; + +  return runp != NULL; +} + +#endif /* thread_dbP.h */ diff --git a/libpthread/pthread.c b/libpthread/pthread.c index bc27aee3f..e91fe0150 100644 --- a/libpthread/pthread.c +++ b/libpthread/pthread.c @@ -2,7 +2,7 @@  /*   * A simple clone based pthread implementation   * - * Copyright (C) 2001 by Erik Andersen <andersee@debian.org> + * Copyright (C) 2001,2002 by Erik Andersen <andersee@debian.org>   *   * 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  | 
