From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/lib/direct/thread.c | 795 ++++++++++++++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100755 Source/DirectFB/lib/direct/thread.c (limited to 'Source/DirectFB/lib/direct/thread.c') diff --git a/Source/DirectFB/lib/direct/thread.c b/Source/DirectFB/lib/direct/thread.c new file mode 100755 index 0000000..91c66fc --- /dev/null +++ b/Source/DirectFB/lib/direct/thread.c @@ -0,0 +1,795 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + This 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 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +D_DEBUG_DOMAIN( Direct_Thread, "Direct/Thread", "Thread management" ); +D_DEBUG_DOMAIN( Direct_ThreadInit, "Direct/Thread/Init", "Thread initialization" ); + + +/* FIXME: DIRECT_THREAD_WAIT_INIT is required, but should be optional. */ +#define DIRECT_THREAD_WAIT_INIT + + +struct __D_DirectThread { + int magic; + + pthread_t thread; /* The pthread thread identifier. */ + pid_t tid; + + char *name; + + DirectThreadType type; /* The thread's type, e.g. input thread. */ + DirectThreadMainFunc main; /* The thread's main routine (or entry point). */ + void *arg; /* Custom argument passed to the main routine. */ + + bool canceled; /* Set when direct_thread_cancel() is called. */ + bool joining; /* Set when direct_thread_join() is called. */ + bool joined; /* Set when direct_thread_join() has finished. */ + bool detached; /* Set when direct_thread_detach() is called. */ + bool terminated; /* Set when direct_thread_terminate() is called. */ + +#ifdef DIRECT_THREAD_WAIT_INIT + bool init; /* Set to true before calling the main routine. */ +#endif + + pthread_mutex_t lock; + pthread_cond_t cond; + + unsigned int counter; +}; + +struct __D_DirectThreadInitHandler { + DirectLink link; + + int magic; + + DirectThreadInitFunc func; + void *arg; +}; + +/******************************************************************************/ + +/* + * Wrapper around pthread's main routine to pass additional arguments + * and setup things like signal masks and scheduling priorities. + */ +static void *direct_thread_main( void *arg ); + +/******************************************************************************/ + +static pthread_mutex_t handler_lock = PTHREAD_MUTEX_INITIALIZER; +static DirectLink *handlers = NULL; + +/******************************************************************************/ + +DirectThreadInitHandler * +direct_thread_add_init_handler( DirectThreadInitFunc func, + void *arg ) +{ + DirectThreadInitHandler *handler; + + handler = D_CALLOC( 1, sizeof(DirectThreadInitHandler) ); + if (!handler) { + D_WARN( "out of memory" ); + return NULL; + } + + handler->func = func; + handler->arg = arg; + + D_MAGIC_SET( handler, DirectThreadInitHandler ); + + pthread_mutex_lock( &handler_lock ); + + direct_list_append( &handlers, &handler->link ); + + pthread_mutex_unlock( &handler_lock ); + + return handler; +} + +void +direct_thread_remove_init_handler( DirectThreadInitHandler *handler ) +{ + D_MAGIC_ASSERT( handler, DirectThreadInitHandler ); + + pthread_mutex_lock( &handler_lock ); + + direct_list_remove( &handlers, &handler->link ); + + pthread_mutex_unlock( &handler_lock ); + + D_MAGIC_CLEAR( handler ); + + D_FREE( handler ); +} + +/******************************************************************************/ + +static pthread_mutex_t key_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_key_t thread_key = -1; + +/******************************************************************************/ + +DirectThread * +direct_thread_create( DirectThreadType thread_type, + DirectThreadMainFunc thread_main, + void *arg, + const char *name ) +{ + DirectThread *thread; + pthread_attr_t attr; + struct sched_param param; + int policy; + int priority; + size_t stack_size; + + D_ASSERT( thread_main != NULL ); + D_ASSERT( name != NULL ); + + D_DEBUG_AT( Direct_Thread, "%s( %s, %p(%p), '%s' )\n", __FUNCTION__, + direct_thread_type_name(thread_type), thread_main, arg, name ); + + /* Create the key for the TSD (thread specific data). */ + pthread_mutex_lock( &key_lock ); + + if (thread_key == -1) + pthread_key_create( &thread_key, NULL ); + + pthread_mutex_unlock( &key_lock ); + + /* Allocate thread structure. */ + thread = D_CALLOC( 1, sizeof(DirectThread) ); + if (!thread) { + D_OOM(); + return NULL; + } + + /* Write thread information to structure. */ + thread->name = D_STRDUP( name ); + thread->type = thread_type; + thread->main = thread_main; + thread->arg = arg; + + /* Initialize to -1 for synchronization. */ + thread->thread = (pthread_t) -1; + thread->tid = (pid_t) -1; + + /* Initialize mutex and condition. */ + direct_util_recursive_pthread_mutex_init( &thread->lock ); + pthread_cond_init( &thread->cond, NULL ); + + D_MAGIC_SET( thread, DirectThread ); + + /* Initialize scheduling and other parameters. */ + pthread_attr_init( &attr ); + +#ifdef PTHREAD_EXPLICIT_SCHED + pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); +#endif + + /* Select scheduler. */ + switch (direct_config->thread_scheduler) { + case DCTS_FIFO: + policy = SCHED_FIFO; + break; + + case DCTS_RR: + policy = SCHED_RR; + break; + + default: + policy = SCHED_OTHER; + break; + } + + if (pthread_attr_setschedpolicy( &attr, policy )) + D_PERROR( "Direct/Thread: Could not set scheduling policy to %s!\n", direct_thread_policy_name(policy) ); + + /* Read (back) value. */ + pthread_attr_getschedpolicy( &attr, &policy ); + + /* Select priority. */ + switch (thread->type) { + case DTT_CLEANUP: + case DTT_INPUT: + case DTT_OUTPUT: + case DTT_MESSAGING: + case DTT_CRITICAL: + priority = thread->type * direct_config->thread_priority_scale / 100; + break; + + default: + priority = direct_config->thread_priority; + break; + } + + D_DEBUG_AT( Direct_ThreadInit, " -> %s (%d) [%d;%d]\n", direct_thread_policy_name(policy), priority, + sched_get_priority_min( policy ), sched_get_priority_max( policy ) ); + + if (priority < sched_get_priority_min( policy )) + priority = sched_get_priority_min( policy ); + + if (priority > sched_get_priority_max( policy )) + priority = sched_get_priority_max( policy ); + + param.sched_priority = priority; + + if (pthread_attr_setschedparam( &attr, ¶m )) + D_PERROR( "Direct/Thread: Could not set scheduling priority to %d!\n", priority ); + + /* Select stack size? */ + if (direct_config->thread_stack_size > 0) { + if (pthread_attr_setstacksize( &attr, direct_config->thread_stack_size )) + D_PERROR( "Direct/Thread: Could not set stack size to %d!\n", direct_config->thread_stack_size ); + } + + /* Read (back) value. */ + pthread_attr_getstacksize( &attr, &stack_size ); + + /* Lock the thread mutex. */ + D_DEBUG_AT( Direct_ThreadInit, " -> locking...\n" ); + pthread_mutex_lock( &thread->lock ); + + /* Create and run the thread. */ + D_DEBUG_AT( Direct_ThreadInit, " -> creating...\n" ); + pthread_create( &thread->thread, &attr, direct_thread_main, thread ); + + pthread_attr_destroy( &attr ); + + pthread_getschedparam( thread->thread, &policy, ¶m ); + + D_INFO( "Direct/Thread: Started '%s' (%d) [%s %s/%s %d/%d] <%zu>...\n", + name, thread->tid, direct_thread_type_name(thread_type), + direct_thread_policy_name(policy), direct_thread_scheduler_name(direct_config->thread_scheduler), + param.sched_priority, priority, stack_size ); + +#ifdef DIRECT_THREAD_WAIT_INIT + /* Wait for completion of the thread's initialization. */ + while (!thread->init) { + D_DEBUG_AT( Direct_ThreadInit, " -> waiting...\n" ); + pthread_cond_wait( &thread->cond, &thread->lock ); + } + + D_DEBUG_AT( Direct_ThreadInit, " -> ...thread is running.\n" ); +#endif + + /* Unlock the thread mutex. */ + D_DEBUG_AT( Direct_ThreadInit, " -> unlocking...\n" ); + pthread_mutex_unlock( &thread->lock ); + + D_DEBUG_AT( Direct_ThreadInit, " -> returning %p\n", thread ); + + return thread; +} + +DirectThread * +direct_thread_self( void ) +{ + DirectThread *thread = pthread_getspecific( thread_key ); + + if (thread) + D_MAGIC_ASSERT( thread, DirectThread ); + + return thread; +} + +const char * +direct_thread_get_name( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->name != NULL ); + + return thread->name; +} + +pid_t +direct_thread_get_tid( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + + return thread->tid; +} + +__attribute__((no_instrument_function)) +const char * +direct_thread_self_name( void ) +{ + DirectThread *thread = pthread_getspecific( thread_key ); + + /* + * This function is called by debugging functions, e.g. debug messages, assertions etc. + * Therefore no assertions are made here, because they would loop forever if they fail. + */ + +#ifndef MACOS + return thread ? thread->name : NULL; +#else + return "debug"; +#endif +} + +void +direct_thread_set_name( const char *name ) +{ + char *copy; + DirectThread *thread = pthread_getspecific( thread_key ); + + D_DEBUG_AT( Direct_Thread, "%s( '%s' )\n", __FUNCTION__, name ); + + /* Support this function for non-direct threads. */ + if (!thread) { + D_DEBUG_AT( Direct_Thread, " -> attaching unknown thread %d\n", direct_gettid() ); + + /* Create the key for the TSD (thread specific data). */ + pthread_mutex_lock( &key_lock ); + + if (thread_key == -1) + pthread_key_create( &thread_key, NULL ); + + pthread_mutex_unlock( &key_lock ); + + + thread = D_CALLOC( 1, sizeof(DirectThread) ); + if (!thread) { + D_OOM(); + return; + } + + thread->thread = pthread_self(); + thread->tid = direct_gettid(); + + D_MAGIC_SET( thread, DirectThread ); + + pthread_setspecific( thread_key, thread ); + } + else + D_DEBUG_AT( Direct_Thread, " -> was '%s' (%d)\n", thread->name, direct_gettid() ); + + /* Duplicate string. */ + copy = D_STRDUP( name ); + if (!copy) { + D_OOM(); + return; + } + + /* Free old string. */ + if (thread->name) + D_FREE( thread->name ); + + /* Keep the copy. */ + thread->name = copy; +} + +DirectResult +direct_thread_wait( DirectThread *thread, int timeout_ms ) +{ + unsigned int old_counter = thread->counter; + + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d, %dms )\n", __FUNCTION__, + thread->main, thread->name, thread->tid, timeout_ms ); + + while (old_counter == thread->counter && !thread->terminated) + pthread_cond_wait( &thread->cond, &thread->lock ); + + if (thread->terminated) + return DR_DEAD; + + return DR_OK; +} + +void +direct_thread_notify( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + pthread_mutex_lock( &thread->lock ); + + thread->counter++; + + pthread_mutex_unlock( &thread->lock ); + + pthread_cond_broadcast( &thread->cond ); +} + +void +direct_thread_lock( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + pthread_mutex_lock( &thread->lock ); +} + +void +direct_thread_unlock( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + pthread_mutex_unlock( &thread->lock ); +} + +void +direct_thread_terminate( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + D_ASSUME( !pthread_equal( thread->thread, pthread_self() ) ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + thread->terminated = true; + + direct_thread_notify( thread ); +} + +void +direct_thread_cancel( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + D_ASSERT( !pthread_equal( thread->thread, pthread_self() ) ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + thread->canceled = true; + +#if DIRECT_BUILD_NO_PTHREAD_CANCEL + D_UNIMPLEMENTED(); +#else + pthread_cancel( thread->thread ); +#endif +} + +bool +direct_thread_is_canceled( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + + return thread->canceled; +} + +void +direct_thread_detach( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + D_ASSERT( !pthread_equal( thread->thread, pthread_self() ) ); + + D_ASSUME( !thread->canceled ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + thread->detached = true; + + pthread_detach( thread->thread ); +} + +bool +direct_thread_is_detached( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + + return thread->detached; +} + +void +direct_thread_testcancel( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + D_ASSERT( pthread_equal( thread->thread, pthread_self() ) ); + +#if DIRECT_BUILD_NO_PTHREAD_CANCEL + D_UNIMPLEMENTED(); +#else + /* Quick check before calling the pthread function. */ + if (thread->canceled) + pthread_testcancel(); +#endif +} + +void +direct_thread_join( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSERT( thread->thread != -1 ); + + D_ASSUME( !pthread_equal( thread->thread, pthread_self() ) ); + D_ASSUME( !thread->joining ); + D_ASSUME( !thread->joined ); + D_ASSUME( !thread->detached ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + if (thread->detached) { + D_DEBUG_AT( Direct_Thread, " -> DETACHED!\n" ); + return; + } + + if (!thread->joining && !pthread_equal( thread->thread, pthread_self() )) { + thread->joining = true; + + D_DEBUG_AT( Direct_Thread, " -> joining...\n" ); + + pthread_join( thread->thread, NULL ); + + thread->joined = true; + + D_DEBUG_AT( Direct_Thread, " -> joined.\n" ); + } +} + +bool +direct_thread_is_joined( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + + return thread->joined; +} + +void +direct_thread_destroy( DirectThread *thread ) +{ + D_MAGIC_ASSERT( thread, DirectThread ); + D_ASSUME( !pthread_equal( thread->thread, pthread_self() ) ); + D_ASSUME( !thread->detached ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + if (thread->detached) { + D_DEBUG_AT( Direct_Thread, " -> DETACHED!\n" ); + return; + } + + if (!thread->joined && !pthread_equal( thread->thread, pthread_self() )) { + if (thread->canceled) + D_DEBUG_AT( Direct_Thread, " -> cancled but not joined!\n" ); + else { + D_DEBUG_AT( Direct_Thread, " -> still running!\n" ); + + if (thread->name) + D_ERROR( "Direct/Thread: Canceling '%s' (%d)!\n", thread->name, thread->tid ); + else + D_ERROR( "Direct/Thread: Canceling %d!\n", thread->tid ); + + thread->detached = true; + + pthread_detach( thread->thread ); + + direct_thread_cancel( thread ); + + return; + } + } + + D_MAGIC_CLEAR( thread ); + + D_FREE( thread->name ); + D_FREE( thread ); +} + +/******************************************************************************/ + +#if DIRECT_BUILD_TEXT +const char * +direct_thread_type_name( DirectThreadType type ) +{ + switch (type) { + case DTT_DEFAULT: + return "DEFAULT"; + + case DTT_CLEANUP: + return "CLEANUP"; + + case DTT_INPUT: + return "INPUT"; + + case DTT_OUTPUT: + return "OUTPUT"; + + case DTT_MESSAGING: + return "MESSAGING"; + + case DTT_CRITICAL: + return "CRITICAL"; + } + + return ""; +} + +const char * +direct_thread_scheduler_name( DirectConfigThreadScheduler scheduler ) +{ + switch (scheduler) { + case DCTS_OTHER: + return "OTHER"; + + case DCTS_FIFO: + return "FIFO"; + + case DCTS_RR: + return "RR"; + } + + return ""; +} + +const char * +direct_thread_policy_name( int policy ) +{ + switch (policy) { + case SCHED_OTHER: + return "OTHER"; + + case SCHED_FIFO: + return "FIFO"; + + case SCHED_RR: + return "RR"; + } + + return ""; +} +#endif + +/******************************************************************************/ + +static void +direct_thread_cleanup( void *arg ) +{ + DirectThread *thread = arg; + + D_MAGIC_ASSERT( thread, DirectThread ); + + D_DEBUG_AT( Direct_Thread, "%s( %p, '%s' %d )\n", __FUNCTION__, thread->main, thread->name, thread->tid ); + + if (thread->detached) { + D_MAGIC_CLEAR( thread ); + + D_FREE( thread->name ); + D_FREE( thread ); + } +} + +/******************************************************************************/ + +static void * +direct_thread_main( void *arg ) +{ + void *ret; + DirectThread *thread = arg; + DirectThreadInitHandler *handler; + pid_t tid; + + tid = direct_gettid(); + + D_DEBUG_AT( Direct_ThreadInit, "%s( %p ) <- tid %d\n", __FUNCTION__, arg, tid ); + + D_DEBUG_AT( Direct_ThreadInit, " -> starting...\n" ); + + D_MAGIC_ASSERT( thread, DirectThread ); + + pthread_cleanup_push( direct_thread_cleanup, thread ); + + + pthread_setspecific( thread_key, thread ); + + thread->tid = tid; + + + /* Call all init handlers. */ + pthread_mutex_lock( &handler_lock ); + + direct_list_foreach (handler, handlers) + handler->func( thread, handler->arg ); + + pthread_mutex_unlock( &handler_lock ); + + + /* Have all signals handled by the main thread. */ + if (direct_config->thread_block_signals) + direct_signals_block_all(); + + /* Lock the thread mutex. */ + D_DEBUG_AT( Direct_ThreadInit, " -> locking...\n" ); + pthread_mutex_lock( &thread->lock ); + + /* Indicate that our initialization has completed. */ + thread->init = true; + +#ifdef DIRECT_THREAD_WAIT_INIT + D_DEBUG_AT( Direct_ThreadInit, " -> signalling...\n" ); + pthread_cond_signal( &thread->cond ); +#endif + + /* Unlock the thread mutex. */ + D_DEBUG_AT( Direct_ThreadInit, " -> unlocking...\n" ); + pthread_mutex_unlock( &thread->lock ); + + if (thread->joining) { + D_DEBUG_AT( Direct_Thread, " -> Being joined before entering main routine!\n" ); + return NULL; + } + + D_MAGIC_ASSERT( thread, DirectThread ); + + /* Call main routine. */ + D_DEBUG_AT( Direct_ThreadInit, " -> running...\n" ); + ret = thread->main( thread, thread->arg ); + + D_DEBUG_AT( Direct_Thread, " -> Returning %p from '%s' (%s, %d)...\n", + ret, thread->name, direct_thread_type_name(thread->type), thread->tid ); + + D_MAGIC_ASSERT( thread, DirectThread ); + + pthread_cleanup_pop( 1 ); + + return ret; +} + -- cgit