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 |