From 785ea306c1f4757224afe3172ecb2eb1c97263e5 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 2 Jul 2006 02:31:21 +0000 Subject: [PATCH] Threading support. --- Makefile.am | 2 + acx_pthread.m4 | 232 ++++++++++++++++++++++ configure.ac | 24 ++- xmltooling/Makefile.am | 10 +- xmltooling/util/PThreads.cpp | 301 +++++++++++++++++++++++++++++ xmltooling/util/Threads.h | 309 ++++++++++++++++++++++++++++++ xmltooling/util/Win32Threads.cpp | 404 +++++++++++++++++++++++++++++++++++++++ xmltooling/xmltooling.vcproj | 8 + 8 files changed, 1281 insertions(+), 9 deletions(-) create mode 100644 acx_pthread.m4 create mode 100644 xmltooling/util/PThreads.cpp create mode 100644 xmltooling/util/Threads.h create mode 100644 xmltooling/util/Win32Threads.cpp diff --git a/Makefile.am b/Makefile.am index 2afd6ff..76dd4cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,8 @@ EXTRA_DIST = $(DX_CONFIG) \ doc \ schemas \ cpp-xmltooling.sln \ + acx_pthread.m4 \ + acinclude.m4 \ libtool.m4 \ doxygen.m4 \ depcomp \ diff --git a/acx_pthread.m4 b/acx_pthread.m4 new file mode 100644 index 0000000..ba6c09c --- /dev/null +++ b/acx_pthread.m4 @@ -0,0 +1,232 @@ +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl This macro figures out how to build C programs using POSIX +dnl threads. It sets the PTHREAD_LIBS output variable to the threads +dnl library and linker flags, and the PTHREAD_CFLAGS output variable +dnl to any special C compiler flags that are needed. (The user can also +dnl force certain compiler flags/libs to be tested by setting these +dnl environment variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl If you are only building threads programs, you may wish to +dnl use these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE +dnl to that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands +dnl to run it if it is not found. If ACTION-IF-FOUND is not specified, +dnl the default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, +dnl or if you have any other suggestions or comments. This macro was +dnl based on work by SGJ on autoconf scripts for FFTW (www.fftw.org) +dnl (with help from M. Frigo), as well as ac_pthread and hb_pthread +dnl macros posted by AFC to the autoconf macro repository. We are also +dnl grateful for the helpful feedback of numerous users. +dnl +dnl @author Steven G. Johnson and Alejandro Forero Cuervo + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + *-osf* | *-hpux*) flag="-D_REENTRANT";; + *solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/configure.ac b/configure.ac index bcb59f9..cf3b09c 100644 --- a/configure.ac +++ b/configure.ac @@ -47,14 +47,6 @@ fi AC_DISABLE_STATIC AC_PROG_LIBTOOL -AC_LANG(C++) - -# C++ requirements -AC_CXX_REQUIRE_STL -AC_CXX_NAMESPACES - -AC_LANG(C) - # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T @@ -64,8 +56,24 @@ AC_CHECK_FUNCS([strchr strdup strstr]) AC_CHECK_HEADERS([dlfcn.h]) AC_CHECK_FUNC(dlclose, , [ AC_CHECK_LIB(dl, dlopen) ]) +# checks for pthreads +ACX_PTHREAD([enable_threads="pthread"],[enable_threads="no"]) +if test $enable_threads != "pthread"; then + AC_MSG_ERROR([unable to find pthreads, currently this is required]) +else + AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]) + AM_CONDITIONAL(BUILD_PTHREAD,test "$enable_threads" = "pthread") + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$PTHREAD_CFLAGS $CFLAGS" + CXXFLAGS="$PTHREAD_CFLAGS $CXXFLAGS" +fi + AC_LANG(C++) +# C++ requirements +AC_CXX_REQUIRE_STL +AC_CXX_NAMESPACES + # log4cpp settings AC_PATH_PROG(LOG4CPP_CONFIG,log4cpp-config) AC_ARG_WITH(log4cpp, diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index 0e8cdfe..9681b81 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -65,6 +65,7 @@ utilinclude_HEADERS = \ util/DateTime.h \ util/NDC.h \ util/ParserPool.h \ + util/Threads.h \ util/XMLConstants.h \ util/XMLHelper.h \ util/XMLObjectChildrenList.h @@ -87,6 +88,12 @@ else xmlsec_sources = endif +if BUILD_PTHREAD +thread_sources = PThread.cpp +else +thread_sources = +endif + libxmltooling_la_SOURCES = \ AbstractAttributeExtensibleXMLObject.cpp \ AbstractChildlessElement.cpp \ @@ -114,7 +121,8 @@ libxmltooling_la_SOURCES = \ util/XMLHelper.cpp \ validation/AbstractValidatingXMLObject.cpp \ validation/Validator.cpp \ - ${xmlsec_sources} + ${xmlsec_sources} \ + $(thread_sources) # this is different from the project version # http://sources.redhat.com/autobook/autobook/autobook_91.html diff --git a/xmltooling/util/PThreads.cpp b/xmltooling/util/PThreads.cpp new file mode 100644 index 0000000..0678e2e --- /dev/null +++ b/xmltooling/util/PThreads.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2001-2005 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * PThreads.cpp + * + * Thread and locking wrappers for POSIX platforms + */ + +#include "internal.h" +#include "util/Threads.h" + +#include +#include + +#ifdef HAVE_PTHREAD +# include +# ifndef HAVE_PTHREAD_RWLOCK_INIT +# include +# endif +#else +# error "This implementation is for POSIX platforms." +#endif + +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace xmltooling { + + class XMLTOOL_DLLLOCAL ThreadImpl : public Thread { + pthread_t thread_id; + public: + ThreadImpl(void* (*start_routine)(void*), void* arg); + virtual ~ThreadImpl() {} + + int detach() { + return pthread_detach(thread_id); + } + + int join(void** thread_return) { + return pthread_join(thread_id, thread_return); + } + + int kill(int signo) { + return pthread_kill(thread_id, signo); + } + }; + + class XMLTOOL_DLLLOCAL MutexImpl : public Mutex { + pthread_mutex_t mutex; + public: + MutexImpl(); + virtual ~MutexImpl() { + pthread_mutex_destroy(&mutex); + } + + int lock() { + return pthread_mutex_lock(&mutex); + } + + int unlock() { + return pthread_mutex_unlock(&mutex); + } + }; + + class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait { + pthread_cond_t cond; + public: + CondWaitImpl(); + virtual ~CondWaitImpl() { + pthread_cond_destroy(&cond); + } + + int wait(Mutex* mutex) { + return wait(static_cast(mutex)); + } + + int wait(MutexImpl* mutex) { + return pthread_cond_wait(&cond, &(mutex->mutex)); + } + + int timedwait(Mutex* mutex, int delay_seconds) { + return timedwait(static_cast(mutex), delay_seconds); + } + + int timedwait(MutexImpl* mutex, int delay_seconds) { + struct timespec ts; + memset(&ts, 0, sizeof(ts)); + ts.tv_sec = time(NULL) + delay_seconds; + return pthread_cond_timedwait(&cond, &(mutex->mutex), &ts); + } + + int signal() { + return pthread_cond_signal(&cond); + } + + int broadcast() { + return pthread_cond_broadcast(&cond); + } + }; + + class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock { +#ifdef HAVE_PTHREAD_RWLOCK_INIT + pthread_rwlock_t lock; + public: + RWLockImpl(); + virtual ~RWLockImpl() { + pthread_rwlock_destroy(&lock); + } + + int rdlock() { + return pthread_rwlock_rdlock(&lock); + } + + int wrlock() { + return pthread_rwlock_wrlock(&lock); + } + + int unlock() { + return pthread_rwlock_unlock(&lock); + } +#else + rwlock_t lock; + public: + RWLockImpl(); + virtual ~RWLockImpl() { + rwlock_destroy (&lock); + } + + int rdlock() { + return rw_rdlock(&lock); + } + + int wrlock() { + return rw_wrlock(&lock); + } + + int unlock() { + return rw_unlock(&lock); + } +#endif + }; + + class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey { + pthread_key_t key; + public: + ThreadKeyImpl(void (*destroy_fcn)(void*)); + virtual ~ThreadKeyImpl() { + pthread_key_delete(key); + } + + int setData(void* data) { + return pthread_setspecific(key,data); + } + + void* getData() const { + return pthread_getspecific(key); + } + }; + +}; + +ThreadImpl::ThreadImpl(void* (*start_routine)(void*), void* arg) +{ + int rc=pthread_create(&thread_id, NULL, start_routine, arg); + if (rc) { +#ifdef HAVE_STRERROR_R + char buf[256]; + strerror_r(rc,buf,sizeof(buf)); + buf[255]=0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_create error (%d): %s",rc,buf); +#else + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_create error (%d): %s",rc,strerror(rc)); +#endif + throw ThreadingException("Thread creation failed."); + } +} + +MutexImpl::MutexImpl() +{ + int rc=pthread_mutex_init(&mutex, NULL); + if (rc) { +#ifdef HAVE_STRERROR_R + char buf[256]; + strerror_r(rc,buf,sizeof(buf)); + buf[255]=0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_mutex_init error (%d): %s",rc,buf); +#else + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_mutex_init error (%d): %s",rc,strerror(rc)); +#endif + throw ThreadingException("Mutex creation failed."); + } +} + +CondWaitImpl::CondWaitImpl() +{ + int rc=pthread_cond_init(&cond, NULL); + if (rc) { +#ifdef HAVE_STRERROR_R + char buf[256]; + strerror_r(rc,buf,sizeof(buf)); + buf[255]=0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_cond_init error (%d): %s",rc,buf); +#else + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_cond_init error (%d): %s",rc,strerror(rc)); +#endif + throw ThreadingException("Condition variable creation failed."); + } +} + +RWLockImpl::RWLockImpl() +{ +#ifdef HAVE_PTHREAD_RWLOCK_INIT + int rc=pthread_rwlock_init(&lock, NULL); +#else + int rc=rwlock_init(&lock, USYNC_THREAD, NULL); +#endif + if (rc) { +#ifdef HAVE_STRERROR_R + char buf[256]; + strerror_r(rc,buf,sizeof(buf)); + buf[255]=0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_rwlock_init error (%d): %s",rc,buf); +#else + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_rwlock_init error (%d): %s",rc,strerror(rc)); +#endif + throw ThreadingException("Shared lock creation failed."); + } +} + +ThreadKeyImpl::ThreadKeyImpl(void (*destroy_fcn)(void*)) +{ + int rc=pthread_key_create(&key, destroy_fcn); + if (rc) { +#ifdef HAVE_STRERROR_R + char buf[256]; + strerror_r(rc,buf,sizeof(buf)); + buf[255]=0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_key_create error (%d): %s",rc,buf); +#else + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("pthread_key_create error (%d): %s",rc,strerror(rc)); +#endif + throw ThreadingException("Thread key creation failed."); + } +} + +Thread* Thread::create(void* (*start_routine)(void*), void* arg) +{ + return new ThreadImpl(start_routine, arg); +} + +void Thread::exit(void* return_val) +{ + pthread_exit(return_val); +} + +void Thread::mask_all_signals(void) +{ + sigset_t sigmask; + sigfillset(&sigmask); + Thread::mask_signals(SIG_BLOCK, &sigmask, NULL); +} + +int Thread::mask_signals(int how, const sigset_t *newmask, sigset_t *oldmask) +{ + return pthread_sigmask(how,newmask,oldmask); +} + +Mutex * Mutex::create() +{ + return new MutexImpl(); +} + +CondWait * CondWait::create() +{ + return new CondWaitImpl(); +} + +RWLock * RWLock::create() +{ + return new RWLockImpl(); +} + +ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*)) +{ + return new ThreadKeyImpl(destroy_fcn); +} diff --git a/xmltooling/util/Threads.h b/xmltooling/util/Threads.h new file mode 100644 index 0000000..ef9231b --- /dev/null +++ b/xmltooling/util/Threads.h @@ -0,0 +1,309 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Threads.h + * + * Thread and locking wrappers + */ + +#ifndef _xmltooling_threads_h +#define _xmltooling_threads_h + +#include +#include + +namespace xmltooling +{ + DECL_XMLTOOLING_EXCEPTION(ThreadingException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmltooling,XMLToolingException,Exceptions during threading/locking operations); + + /** + * A class for manual thread creation and synchronization. + */ + class XMLTOOL_API Thread + { + MAKE_NONCOPYABLE(Thread); + public: + Thread() {} + virtual ~Thread() {} + + /** + * Disassociate from the thread. + * + * @return 0 for success, non-zero for failure + */ + virtual int detach()=0; + + /** + * Join with the thread and wait for its completion. + * + * @param thread_return holds the return value of the thread routine + * @return 0 for success, non-zero for failure + */ + virtual int join(void** thread_return)=0; + + /** + * Kill the thread. + * + * @param signo the signal to send to the thread + * @return 0 for success, non-zero for failure + */ + virtual int kill(int signo)=0; + + /** + * Creates a new thread object to run the supplied start routine. + * + * @param start_routine the function to execute on the thread + * @param arg a parameter for the start routine + * @return the created and running thread object + */ + static Thread* create(void* (*start_routine)(void*), void* arg); + + /** + * Exits a thread gracefully. + * + * @param return_val the return value for the thread + */ + static void exit(void* return_val); + +#ifndef WIN32 + /** + * Masks all signals from a thread. + */ + static void mask_all_signals(void); + + /** + * Masks specific signals from a thread. + * + * @param how + * @param newmask the new signal mask + * @param oldmask the old signal mask + * @return 0 for success, non-zero for failure + */ + static int mask_signals(int how, const sigset_t *newmask, sigset_t *oldmask); +#endif + }; + + /** + * A class for managing Thread Local Storage values. + */ + class XMLTOOL_API ThreadKey + { + MAKE_NONCOPYABLE(ThreadKey); + public: + ThreadKey() {} + virtual ~ThreadKey() {} + + /** + * Sets the value for a TLS key. + * + * @param data the value to set + * @return 0 for success, non-zero for failure + */ + virtual int setData(void* data)=0; + + /** + * Returns the value for a TLS key. + * + * @return the value or NULL + */ + virtual void* getData() const=0; + + /** + * Creates a new TLS key. + * + * @param destroy_fn a functon to cleanup key values + * @return the new key + */ + static ThreadKey* create(void (*destroy_fn)(void*)); + }; + + /** + * A class for managing exclusive access to resources. + */ + class XMLTOOL_API Mutex + { + MAKE_NONCOPYABLE(Mutex); + public: + Mutex() {} + virtual ~Mutex() {} + + /** + * Locks the mutex for exclusive access. + * + * @return 0 for success, non-zero for failure + */ + virtual int lock()=0; + + /** + * Unlocks the mutex for exclusive access. + * + * @return 0 for success, non-zero for failure + */ + virtual int unlock()=0; + + /** + * Creates a new mutex object. + * + * @return the new mutex + */ + static Mutex* create(); + }; + + /** + * A class for managing shared and exclusive access to resources. + */ + class XMLTOOL_API RWLock + { + MAKE_NONCOPYABLE(RWLock); + public: + RWLock() {} + virtual ~RWLock() {} + + /** + * Obtains a shared lock. + * + * @return 0 for success, non-zero for failure + */ + virtual int rdlock()=0; + + /** + * Obtains an exclusive lock. + * + * @return 0 for success, non-zero for failure + */ + virtual int wrlock()=0; + + /** + * Unlocks the lock. + * + * @return 0 for success, non-zero for failure + */ + virtual int unlock()=0; + + /** + * Creates a new read/write lock. + * + * @return the new lock + */ + static RWLock* create(); + }; + + /** + * A class for establishing queues on a mutex based on a periodic condition. + */ + class XMLTOOL_API CondWait + { + MAKE_NONCOPYABLE(CondWait); + public: + CondWait() {} + virtual ~CondWait() {} + + /** + * Waits for a condition variable using the supplied mutex as a queue. + * + * @param lock mutex to queue on + * @return 0 for success, non-zero for failure + */ + virtual int wait(Mutex* lock)=0; + + /** + * Waits for a condition variable using the supplied mutex as a queue, + * but only for a certain time limit. + * + * @param lock mutex to queue on + * @param delay_seconds maximum time to wait before waking up + * @return 0 for success, non-zero for failure + */ + virtual int timedwait(Mutex* lock, int delay_seconds)=0; + + /** + * Signal a single thread to wake up if a condition changes. + * + * @return 0 for success, non-zero for failure + */ + virtual int signal()=0; + + /** + * Signal all threads to wake up if a condition changes. + * + * @return 0 for success, non-zero for failure + */ + virtual int broadcast()=0; + + /** + * Creates a new condition variable. + * + * @return the new condition variable + */ + static CondWait* create(); + }; + + /** + * RAII wrapper for a mutex lock. + */ + class XMLTOOL_API Lock { + MAKE_NONCOPYABLE(Lock); + public: + /** + * Locks and wraps the designated mutex. + * + * @param mtx mutex to lock + */ + Lock(Mutex* mtx) : mutex(mtx) { + mutex->lock(); + } + + /** + * Unlocks the wrapped mutex. + */ + ~Lock() { + mutex->unlock(); + } + + private: + Mutex* mutex; + }; + + /** + * RAII wrapper for a shared lock. + */ + class XMLTOOL_API ReadLock { + MAKE_NONCOPYABLE(ReadLock); + public: + /** + * Locks and wraps the designated shared lock. + * + * @param lock lock to acquire + */ + ReadLock(RWLock* lock) : rwlock(lock) { + rwlock->rdlock(); + } + + /** + * Unlocks the wrapped shared lock. + */ + ~ReadLock() { + rwlock->unlock(); + } + + private: + RWLock* rwlock; + }; + +} + +#endif /* _xmltooling_threads_h */ diff --git a/xmltooling/util/Win32Threads.cpp b/xmltooling/util/Win32Threads.cpp new file mode 100644 index 0000000..e239a17 --- /dev/null +++ b/xmltooling/util/Win32Threads.cpp @@ -0,0 +1,404 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Win32Threads.cpp + * + * Thread and locking wrappers for Win32 platforms + */ + +#include "internal.h" +#include "util/Threads.h" + +#include + +#ifndef WIN32 +# error "This implementation is for WIN32 platforms." +#endif + +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +// base error code for a routine to return on failure +#define THREAD_ERROR_TIMEOUT (1) +#define THREAD_ERROR_WAKE_OTHER (2) +#define THREAD_ERROR (3) + +// windows returns non zero for success pthreads returns zero +static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) { + if(rc!=0) // success? + return 0; + Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError()); + return THREAD_ERROR; +} + +namespace xmltooling { + + // two levels of classes are needed here + // in case InitializeCriticalSection + // throws an exception we can keep from + // calling the critical_section destructor + // on unitilized data, or it could be done with a flag + struct XMLTOOL_DLLLOCAL critical_section_data { + CRITICAL_SECTION cs; + critical_section_data(){ + InitializeCriticalSection(&cs); + } + }; + + class XMLTOOL_DLLLOCAL critical_section { + private: + critical_section_data cse; + public: + critical_section(){} + ~critical_section(){ + DeleteCriticalSection (&cse.cs); + } + void enter(void) { + EnterCriticalSection(&cse.cs); + } + void leave(void) { + LeaveCriticalSection(&cse.cs); + } + }; + + // hold a critical section over the lifetime of this object + // used to make a stack variable that unlocks automaticly + // on return/throw + class XMLTOOL_DLLLOCAL with_crit_section { + private: + critical_section& cs; + public: + with_crit_section(critical_section& acs):cs(acs){ + cs.enter(); + } + ~with_crit_section(){ + cs.leave(); + } + }; + + class XMLTOOL_DLLLOCAL ThreadImpl : public Thread { + private: + HANDLE thread_id; + public: + ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) { + thread_id=CreateThread( + 0, // security attributes + 0, // use default stack size, maybe this should be setable + (LPTHREAD_START_ROUTINE ) start_routine, + arg, + 0, // flags, default is ignore stacksize and don't create suspended which is what we want + 0); + if (thread_id==0) { + map_windows_error_status_to_pthreads(); + throw ThreadingException("Thread creation failed."); + } + } + + ~ThreadImpl() { + (void)detach(); + } + + int detach() { + if (thread_id==0) + return THREAD_ERROR; + int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id)); + thread_id=0; + return rc; + } + + int join(void** thread_return) { + if (thread_id==0) + return THREAD_ERROR; + if (thread_return!=0) + *thread_return=0; + int rc=WaitForSingleObject(thread_id,INFINITE); + switch(rc) { + case WAIT_OBJECT_0: + if (thread_return) + map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return)); + default: + return THREAD_ERROR; + } + return 0; + } + + int kill(int signo) { + if (thread_id==0) + return THREAD_ERROR; + return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo)); + } + }; + + class XMLTOOL_DLLLOCAL MutexImpl : public Mutex { + private: + HANDLE mhandle; + public: + MutexImpl() : mhandle(CreateMutex(0,false,0)) { + if (mhandle==0) { + map_windows_error_status_to_pthreads(); + throw ThreadingException("Mutex creation failed."); + } + } + + ~MutexImpl() { + if((mhandle!=0) && (!CloseHandle(mhandle))) + map_windows_error_status_to_pthreads(); + } + + int lock() { + int rc=WaitForSingleObject(mhandle,INFINITE); + switch(rc) { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + return 0; + default: + return map_windows_error_status_to_pthreads(); + } + } + + int unlock() { + return map_windows_error_status_to_pthreads(ReleaseMutex(mhandle)); + } + }; + + class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait { + private: + HANDLE cond; + + public: + CondWaitImpl() : cond(CreateEvent(0,false,false,0)) { + if(cond==0) { + map_windows_error_status_to_pthreads(); + throw ThreadingException("Event creation failed."); + } + }; + + ~CondWaitImpl() { + if((cond!=0) && (!CloseHandle(cond))) + map_windows_error_status_to_pthreads(); + } + + int wait(Mutex* mutex) { + return timedwait(mutex,INFINITE); + } + + int signal() { + if(!SetEvent(cond)) + return map_windows_error_status_to_pthreads(); + return 0; + } + + int broadcast() { + throw ThreadingException("Broadcast not implemented on Win32 platforms."); + } + + // wait for myself to signal and this mutex or the timeout + int timedwait(Mutex* mutex, int delay_seconds) { + int rc=mutex->unlock(); + if(rc!=0) + return rc; + + int delay_ms=delay_seconds; + if(delay_seconds!=INFINITE) + delay_ms*=1000; + rc=WaitForSingleObject(cond,delay_ms); + int rc2=mutex->lock(); + if(rc2!=0) + return rc2; + switch(rc) { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + case WAIT_TIMEOUT: + return 0; + default: + return map_windows_error_status_to_pthreads(); + } + return 0; + } + }; + + class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock { + private: + // used to protect read or write to the data below + critical_section cs; + // event handle threads wait on when the lock they want is busy + // normally set to signaled all the time, if some thread can't get what + // they want they reset it and sleep. on releasing a lock set it to + // signaled if someone may have wanted what you just released + HANDLE wake_waiters; + // number of threads holding a read lock + int num_readers; + // true iff there a writer has our lock + bool have_writer; + + public: + RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) { + with_crit_section acs(cs); + wake_waiters=CreateEvent(0,true,true,0); + have_writer=false; + if (wake_waiters==0) { + map_windows_error_status_to_pthreads(); + throw ThreadingException("Event creation for shared lock failed."); + } + } + + ~RWLockImpl() { + with_crit_section acs(cs); + if ((wake_waiters!=0) && (!CloseHandle(wake_waiters))) + map_windows_error_status_to_pthreads(); + } + + int rdlock() { + while(1) { + // wait for the lock maybe being availible + // we will find out for sure inside the critical section + if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) + return map_windows_error_status_to_pthreads(); + + with_crit_section alock(cs); + // invariant not locked for reading and writing + if ((num_readers!=0) && (have_writer)) + return THREAD_ERROR; + // if no writer we can join any existing readers + if (!have_writer) { + num_readers++; + return 0; + } + + // have a writer, mark the synchronization object + // so everyone waits, when the writer unlocks it will wake us + if (!ResetEvent(wake_waiters)) + return map_windows_error_status_to_pthreads(); + } + return THREAD_ERROR; + } + + int wrlock() { + while(1) { + // wait for the lock maybe being availible + // we will find out for sure inside the critical section + if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) + return map_windows_error_status_to_pthreads(); + + with_crit_section bla(cs); + // invariant not locked for reading and writing + if ((num_readers!=0) && (have_writer)) + return THREAD_ERROR; + + // if no writer and no readers we can become the writer + if ((num_readers==0) && (!have_writer)) { + have_writer=true; + return 0; + } + + // lock is busy, the unlocker will wake us + if (!ResetEvent(wake_waiters)) + return map_windows_error_status_to_pthreads(); + } + return THREAD_ERROR; + } + + int unlock() { + with_crit_section mumble(cs); + // invariant not locked for reading and writing + if ((num_readers!=0) && (have_writer)) + return THREAD_ERROR; + + // error if nothing locked + if ((num_readers==0) && (!have_writer)) + return THREAD_ERROR; + + // if there was a writer it has to be us so unlock write lock + have_writer=false; + + // if there where any reades there is one less now + if(num_readers>0) + num_readers--; + + // if no readers left wake up any readers/writers waiting + // to have a go at it + if (num_readers==0) + if (!SetEvent(wake_waiters)) + return map_windows_error_status_to_pthreads(); + return 0; + } + }; + + typedef void (*destroy_hook_type)(void*); + + class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey { + private: + destroy_hook_type destroy_hook; + DWORD key; + + public: + ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) { + key=TlsAlloc(); + }; + + virtual ~ThreadKeyImpl() { + if (destroy_hook) + destroy_hook(TlsGetValue(key)); + TlsFree(key); + } + + int setData(void* data) { + TlsSetValue(key,data); + return 0; + } + + void* getData() const { + return TlsGetValue(key); + } + }; + +}; + +// +// public "static" creation functions +// + +Thread* Thread::create(void* (*start_routine)(void*), void* arg) +{ + return new ThreadImpl(start_routine, arg); +} + +void Thread::exit(void* return_val) +{ + ExitThread((DWORD)return_val); +} + +Mutex * Mutex::create() +{ + return new MutexImpl(); +} + +CondWait * CondWait::create() +{ + return new CondWaitImpl(); +} + +RWLock * RWLock::create() +{ + return new RWLockImpl(); +} + +ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*)) +{ + return new ThreadKeyImpl(destroy_fcn); +} diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 4bed022..34b78c8 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -241,6 +241,10 @@ > + + @@ -447,6 +451,10 @@ > + + -- 2.1.4