diff options
Diffstat (limited to 'src/kernel/qthread_unix.cpp')
-rw-r--r-- | src/kernel/qthread_unix.cpp | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/src/kernel/qthread_unix.cpp b/src/kernel/qthread_unix.cpp new file mode 100644 index 000000000..c79748e26 --- /dev/null +++ b/src/kernel/qthread_unix.cpp @@ -0,0 +1,474 @@ +/**************************************************************************** +** +** TQThread class for Unix +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#if defined(QT_THREAD_SUPPORT) + +#include "qplatformdefs.h" + +typedef pthread_mutex_t Q_MUTEX_T; + +#include "qthread.h" +#include <private/qthreadinstance_p.h> +#include <private/qmutex_p.h> +#include <private/qmutexpool_p.h> +#include <qthreadstorage.h> + +#include <errno.h> +#include <sched.h> + + +static TQThreadInstance main_instance = { + 0, { 0, &main_instance }, 0, 0, 1, 0, PTHREAD_COND_INITIALIZER, 0 +}; + + +static TQMutexPool *qt_thread_mutexpool = 0; + + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +typedef void*(*TQtThreadCallback)(void*); + +static pthread_once_t storage_key_once = PTHREAD_ONCE_INIT; +static pthread_key_t storage_key; +static void create_storage_key() +{ + pthread_key_create( &storage_key, NULL ); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + + +/************************************************************************** + ** TQThreadInstance + *************************************************************************/ + +TQThreadInstance *TQThreadInstance::current() +{ + pthread_once( &storage_key_once, create_storage_key ); + TQThreadInstance *ret = (TQThreadInstance *) pthread_getspecific( storage_key ); + return ret; +} + +void TQThreadInstance::init(unsigned int stackSize) +{ + stacksize = stackSize; + args[0] = args[1] = 0; + thread_storage = 0; + finished = FALSE; + running = FALSE; + orphan = FALSE; + + pthread_cond_init(&thread_done, NULL); + thread_id = 0; + + // threads have not been initialized yet, do it now + if (! qt_thread_mutexpool) TQThread::initialize(); +} + +void TQThreadInstance::deinit() +{ + pthread_cond_destroy(&thread_done); +} + +void *TQThreadInstance::start( void *_arg ) +{ + void **arg = (void **) _arg; + + pthread_once( &storage_key_once, create_storage_key ); + pthread_setspecific( storage_key, arg[1] ); + pthread_cleanup_push( TQThreadInstance::finish, arg[1] ); + pthread_testcancel(); + + ( (TQThread *) arg[0] )->run(); + + pthread_cleanup_pop( TRUE ); + return 0; +} + +void TQThreadInstance::finish( void * ) +{ + TQThreadInstance *d = current(); + + if ( ! d ) { +#ifdef QT_CHECK_STATE + qWarning( "TQThread: internal error: zero data for running thread." ); +#endif // QT_CHECK_STATE + return; + } + + TQMutexLocker locker( d->mutex() ); + d->running = FALSE; + d->finished = TRUE; + d->args[0] = d->args[1] = 0; + + + TQThreadStorageData::finish( d->thread_storage ); + d->thread_storage = 0; + + d->thread_id = 0; + pthread_cond_broadcast(&d->thread_done); + + if (d->orphan) { + d->deinit(); + delete d; + } +} + +TQMutex *TQThreadInstance::mutex() const +{ + return qt_thread_mutexpool ? qt_thread_mutexpool->get( (void *) this ) : 0; +} + +void TQThreadInstance::terminate() +{ + if ( ! thread_id ) return; + pthread_cancel( thread_id ); +} + + +/************************************************************************** + ** TQThread + *************************************************************************/ + +/*! + This returns the thread handle of the currently executing thread. + + \warning The handle returned by this function is used for internal + purposes and should \e not be used in any application code. On + Windows, the returned value is a pseudo handle for the current + thread, and it cannot be used for numerical comparison. +*/ +TQt::HANDLE TQThread::currentThread() +{ + return (HANDLE) pthread_self(); +} + +/*! \internal + Initializes the TQThread system. +*/ +void TQThread::initialize() +{ + if ( ! qt_global_mutexpool ) + qt_global_mutexpool = new TQMutexPool( TRUE, 73 ); + if ( ! qt_thread_mutexpool ) + qt_thread_mutexpool = new TQMutexPool( FALSE, 127 ); + + pthread_once( &storage_key_once, create_storage_key ); + pthread_setspecific( storage_key, &main_instance ); +} + +/*! \internal + Cleans up the TQThread system. +*/ +void TQThread::cleanup() +{ + delete qt_global_mutexpool; + delete qt_thread_mutexpool; + qt_global_mutexpool = 0; + qt_thread_mutexpool = 0; + + TQThreadInstance::finish(&main_instance); + + pthread_once( &storage_key_once, create_storage_key ); + pthread_setspecific( storage_key, 0 ); +} + +/*! + Ends the execution of the calling thread and wakes up any threads + waiting for its termination. +*/ +void TQThread::exit() +{ + pthread_exit( 0 ); +} + +/* \internal + helper function to do thread sleeps, since usleep()/nanosleep() + aren't reliable enough (in terms of behavior and availability) +*/ +static void thread_sleep( struct timespec *ti ) +{ + pthread_mutex_t mtx; + pthread_cond_t cnd; + + pthread_mutex_init(&mtx, 0); + pthread_cond_init(&cnd, 0); + + pthread_mutex_lock( &mtx ); + (void) pthread_cond_timedwait( &cnd, &mtx, ti ); + pthread_mutex_unlock( &mtx ); + + pthread_cond_destroy( &cnd ); + pthread_mutex_destroy( &mtx ); +} + +/*! + System independent sleep. This causes the current thread to sleep + for \a secs seconds. +*/ +void TQThread::sleep( unsigned long secs ) +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + struct timespec ti; + ti.tv_sec = tv.tv_sec + secs; + ti.tv_nsec = ( tv.tv_usec * 1000 ); + thread_sleep( &ti ); +} + +/*! + System independent sleep. This causes the current thread to sleep + for \a msecs milliseconds +*/ +void TQThread::msleep( unsigned long msecs ) +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + struct timespec ti; + + ti.tv_nsec = ( tv.tv_usec + ( msecs % 1000 ) * 1000 ) * 1000; + ti.tv_sec = tv.tv_sec + ( msecs / 1000 ) + ( ti.tv_nsec / 1000000000 ); + ti.tv_nsec %= 1000000000; + thread_sleep( &ti ); +} + +/*! + System independent sleep. This causes the current thread to sleep + for \a usecs microseconds +*/ +void TQThread::usleep( unsigned long usecs ) +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + struct timespec ti; + + ti.tv_nsec = ( tv.tv_usec + ( usecs % 1000000 ) ) * 1000; + ti.tv_sec = tv.tv_sec + ( usecs / 1000000 ) + ( ti.tv_nsec / 1000000000 ); + ti.tv_nsec %= 1000000000; + thread_sleep( &ti ); +} + +/*! + Begins execution of the thread by calling run(), which should be + reimplemented in a TQThread subclass to contain your code. The + operating system will schedule the thread according to the \a + priority argument. + + If you try to start a thread that is already running, this + function will wait until the the thread has finished and then + restart the thread. + + \sa Priority +*/ +void TQThread::start(Priority priority) +{ + TQMutexLocker locker( d->mutex() ); + + if ( d->running ) + pthread_cond_wait(&d->thread_done, &locker.mutex()->d->handle); + d->running = TRUE; + d->finished = FALSE; + + int ret; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) + switch (priority) { + case InheritPriority: + { + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + break; + } + + default: + { + int sched_policy; + if (pthread_attr_getschedpolicy(&attr, &sched_policy) != 0) { + // failed to get the scheduling policy, don't bother + // setting the priority + qWarning("TQThread: cannot determine default scheduler policy"); + break; + } + + int prio_min = sched_get_priority_min(sched_policy); + int prio_max = sched_get_priority_max(sched_policy); + if (prio_min == -1 || prio_max == -1) { + // failed to get the scheduling parameters, don't + // bother setting the priority + qWarning("TQThread: cannot determine scheduler priority range"); + break; + } + + int prio; + switch (priority) { + case IdlePriority: + prio = prio_min; + break; + + case HighestPriority: + prio = prio_max; + break; + + default: + // crudely scale our priority enum values to the prio_min/prio_max + prio = (((prio_max - prio_min) / TimeCriticalPriority) * + priority) + prio_min; + prio = TQMAX(prio_min, TQMIN(prio_max, prio)); + break; + } + + sched_param sp; + sp.sched_priority = prio; + + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedparam(&attr, &sp); + break; + } + } +#endif // _POSIX_THREAD_PRIORITY_SCHEDULING + + if ( d->stacksize > 0 ) { +#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0) + ret = pthread_attr_setstacksize( &attr, d->stacksize ); +#else + ret = ENOSYS; // stack size not supported, automatically fail +#endif // _POSIX_THREAD_ATTR_STACKSIZE + + if ( ret ) { +#ifdef QT_CHECK_STATE + qWarning( "TQThread::start: thread stack size error: %s", strerror( ret ) ) ; +#endif // QT_CHECK_STATE + + // we failed to set the stacksize, and as the documentation states, + // the thread will fail to run... + d->running = FALSE; + d->finished = FALSE; + return; + } + } + + d->args[0] = this; + d->args[1] = d; + ret = pthread_create( &d->thread_id, &attr, (TQtThreadCallback)TQThreadInstance::start, d->args ); +#if defined (Q_OS_HPUX) + if (ret == EPERM) { + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + ret = pthread_create(&d->thread_id, &attr, (TQtThreadCallback)TQThreadInstance::start, d->args); + } +#endif + pthread_attr_destroy( &attr ); + + if ( ret ) { +#ifdef QT_CHECK_STATE + qWarning( "TQThread::start: thread creation error: %s", strerror( ret ) ); +#endif // QT_CHECK_STATE + + d->running = FALSE; + d->finished = FALSE; + d->args[0] = d->args[1] = 0; + } +} + +void TQThread::start() +{ + start(InheritPriority); +} + +/*! + A thread calling this function will block until either of these + conditions is met: + + \list + \i The thread associated with this TQThread object has finished + execution (i.e. when it returns from \l{run()}). This function + will return TRUE if the thread has finished. It also returns + TRUE if the thread has not been started yet. + \i \a time milliseconds has elapsed. If \a time is ULONG_MAX (the + default), then the wait will never timeout (the thread must + return from \l{run()}). This function will return FALSE if the + wait timed out. + \endlist + + This provides similar functionality to the POSIX \c pthread_join() function. +*/ +bool TQThread::wait( unsigned long time ) +{ + TQMutexLocker locker( d->mutex() ); + + if ( d->thread_id == pthread_self() ) { +#ifdef QT_CHECK_STATE + qWarning( "TQThread::wait: thread tried to wait on itself" ); +#endif // QT_CHECK_STATE + + return FALSE; + } + + if ( d->finished || ! d->running ) + return TRUE; + + int ret; + if (time != ULONG_MAX) { + struct timeval tv; + gettimeofday(&tv, 0); + + timespec ti; + ti.tv_nsec = (tv.tv_usec + (time % 1000) * 1000) * 1000; + ti.tv_sec = tv.tv_sec + (time / 1000) + (ti.tv_nsec / 1000000000); + ti.tv_nsec %= 1000000000; + + ret = pthread_cond_timedwait(&d->thread_done, &locker.mutex()->d->handle, &ti); + } else + ret = pthread_cond_wait(&d->thread_done, &locker.mutex()->d->handle); + +#ifdef QT_CHECK_RANGE + if (ret && ret != ETIMEDOUT) + qWarning("Wait condition wait failure: %s",strerror(ret)); +#endif + + return (ret == 0); +} + + +#endif // QT_THREAD_SUPPORT |