Mercurial > flash_v2
diff packages/compat/posix/current/src/pthread.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | 0c2b7be0d798 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/compat/posix/current/src/pthread.cxx @@ -0,0 +1,2021 @@ +//========================================================================== +// +// pthread.cxx +// +// POSIX pthreads implementation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (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.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg, jlarmour +// Date: 2000-03-27 +// Purpose: POSIX pthread implementation +// Description: This file contains the implementation of the POSIX pthread +// functions. +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <pkgconf/posix.h> +#include <pkgconf/isoinfra.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include "pprivate.h" // POSIX private header + +#include <stdlib.h> // malloc(), free() + +#include <cyg/kernel/sched.hxx> // scheduler definitions +#include <cyg/kernel/thread.hxx> // thread definitions +#include <cyg/kernel/mutex.hxx> // mutex definitions +#include <cyg/kernel/clock.hxx> // clock definitions + +#include <cyg/kernel/sched.inl> // scheduler inlines + +//----------------------------------------------------------------------------- +// First check that the configuration contains the elements we need + +#ifndef CYGPKG_KERNEL +#error POSIX pthread need eCos kernel +#endif + +#ifndef CYGSEM_KERNEL_SCHED_MLQUEUE +#error POSIX pthreads need MLQ scheduler +#endif + +#ifndef CYGSEM_KERNEL_SCHED_TIMESLICE +#error POSIX pthreads need timeslicing +#endif + +#ifndef CYGVAR_KERNEL_THREADS_DATA +#error POSIX pthreads need per-thread data +#endif + +//----------------------------------------------------------------------------- +// Internal definitions + +// Handle entry to a pthread package function. +#define PTHREAD_ENTRY() CYG_REPORT_FUNCTYPE( "returning %d" ) + +// Handle entry to a pthread package function with no args. +#define PTHREAD_ENTRY_VOID() CYG_REPORT_FUNCTION() + +// Do a pthread package defined return. This requires the error code to be +// returned as the result of the function. This also gives us a place to +// put any generic tidyup handling needed for things like signal delivery +// and cancellation. +#define PTHREAD_RETURN(err) \ +CYG_MACRO_START \ + CYG_REPORT_RETVAL( err ); \ + return err; \ +CYG_MACRO_END + +// A void variant of the above. +#define PTHREAD_RETURN_VOID \ +CYG_MACRO_START \ + CYG_REPORT_RETURN(); \ + return; \ +CYG_MACRO_END + +// Check that a pointer passed in as an argument is valid and return +// EINVAL if it is not. This should be used to check pointers that are +// required to be valid. Pointers that may optionally be NULL should +// be checked within the function. +#define PTHREAD_CHECK(ptr) if( (ptr) == NULL ) PTHREAD_RETURN(EINVAL); + + +//============================================================================= +// Internal data structures + +// Mutex for controlling access to shared data structures +Cyg_Mutex pthread_mutex CYGBLD_POSIX_INIT; + +// Array of pthread control structures. A pthread_t object is +// "just" an index into this array. +static pthread_info *thread_table[CYGNUM_POSIX_PTHREAD_THREADS_MAX]; + +// Count of number of threads in table. +static int pthread_count = 0; + +// Count of number of threads that have exited and not been reaped. +static int pthreads_exited; + +// Count of number of threads that are waiting to be joined +static int pthreads_tobejoined; + +// Per-thread key allocation. This key map has a 1 bit set for each +// key that is free, zero if it is allocated. +#define KEY_MAP_TYPE cyg_uint32 +#define KEY_MAP_TYPE_SIZE (sizeof(KEY_MAP_TYPE)*8) // in BITS! +static KEY_MAP_TYPE thread_key[PTHREAD_KEYS_MAX/KEY_MAP_TYPE_SIZE]; +static void (*key_destructor[PTHREAD_KEYS_MAX]) (void *); + +// Index of next pthread_info to allocate from thread_table array. +static int thread_info_next = 0; + +// This is used to make pthread_t values unique even when reusing +// a table slot. This allows CYGNUM_POSIX_PTHREAD_THREADS_MAX to range +// up to 1024. +#define THREAD_ID_COOKIE_INC 0x00000400 +#define THREAD_ID_COOKIE_MASK (THREAD_ID_COOKIE_INC-1) +static pthread_t thread_id_cookie = THREAD_ID_COOKIE_INC; + +//----------------------------------------------------------------------------- +// Main thread. + +static char main_stack[CYGNUM_POSIX_MAIN_DEFAULT_STACK_SIZE]; + +// Thread ID of main thread. +static pthread_t main_thread; + +//============================================================================= +// Exported variables + +int pthread_canceled; // pointed to by PTHREAD_CANCELED + +//============================================================================= +// Internal functions + +//----------------------------------------------------------------------------- +// Private version of pthread_self() that returns a pointer to our internal +// control structure. + +pthread_info *pthread_self_info(void) +{ + Cyg_Thread *thread = Cyg_Thread::self(); + + CYG_CHECK_DATA_PTR(thread, "Illegal current thread"); + + pthread_info *info = (pthread_info *)thread->get_data(CYGNUM_KERNEL_THREADS_DATA_POSIX); + +// CYG_CHECK_DATA_PTR(info, "Not a POSIX thread!!!"); + + return info; +} + +externC pthread_info *pthread_info_id( pthread_t id ) +{ + pthread_t index = id & THREAD_ID_COOKIE_MASK; + + pthread_info *info = thread_table[index]; + + // Check for a valid entry + if( info == NULL ) + return NULL; + + // Check that this is a valid entry + if ( info->state == PTHREAD_STATE_FREE || + info->state == PTHREAD_STATE_EXITED ) + return NULL; + + // Check that the entry matches the id + if( info->id != id ) return NULL; + + // Return the pointer + return info; +} + +//----------------------------------------------------------------------------- +// new operator to allow us to invoke the Cyg_Thread constructor on the +// pthread_info.thread_obj array. + +inline void *operator new(size_t size, cyg_uint8 *ptr) { return (void *)ptr; }; + +//----------------------------------------------------------------------------- +// Optional memory allocation functions for pthread stacks. +// If there is an implementation of malloc() available, define pthread_malloc() +// and pthread_free() to use it. Otherwise define them to do nothing. +// In the future we may want to add configuration here to permit thread stacks +// to be allocated in a nominated memory pool separate from the standard malloc() +// pool. Hence the (currently redundant) encapsulation of these functions. + +#if CYGINT_ISO_MALLOC + +static __inline__ CYG_ADDRWORD pthread_malloc( CYG_ADDRWORD size ) +{ + return (CYG_ADDRWORD)malloc( size ); +} + +static __inline__ void pthread_free( CYG_ADDRWORD m ) +{ + free( (void *)m ); +} + +#define PTHREAD_MALLOC + +#else + +#define pthread_malloc(_x_) (0) + +#define pthread_free(_x_) + +#endif + +//----------------------------------------------------------------------------- +// pthread entry function. +// does some housekeeping and then calls the user's start routine. + +static void pthread_entry(CYG_ADDRWORD data) +{ + pthread_info *self = (pthread_info *)data; + + void *retval = self->start_routine(self->start_arg); + + pthread_exit( retval ); +} + +//----------------------------------------------------------------------------- +// Main entry function. +// This is set as the start_routine of the main thread. It invokes main() +// and if it returns, shuts down the system. + +externC void cyg_libc_invoke_main( void ); + +static void *call_main( void * ) +{ + cyg_libc_invoke_main(); + return NULL; // placate compiler +} + +//----------------------------------------------------------------------------- +// POSIX ASR +// This is installed as the ASR for all POSIX threads. + +static void posix_asr( CYG_ADDRWORD data ) +{ + pthread_info *self = (pthread_info *)data; + + // Call into timer subsystem to deliver any pending + // timer expirations. + cyg_posix_timer_asr(self); + + // Call signal subsystem to deliver any signals + cyg_posix_signal_asr(self); + + // Check for cancellation + if( self->cancelpending && + self->cancelstate == PTHREAD_CANCEL_ENABLE && + self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS ) + { + // If we have a pending cancellation, cancellations are + // enabled and we are in asynchronous mode, then we can do the + // cancellation processing. Since pthread_exit() does + // everything we need to do, we just call that here. + + pthread_exit(PTHREAD_CANCELED); + } +} + +//----------------------------------------------------------------------------- +// The (Grim) Reaper. +// This function is called to tidy up and dispose of any threads that have +// exited. This work must be done from a thread other than the one exiting. +// Note: this function _must_ be called with pthread_mutex locked. + +static void pthread_reap() +{ + int i; + + // Loop over the thread table looking for exited threads. The + // pthreads_exited counter springs us out of this once we have + // found then all (and keeps us out if there are none to do). + + for( i = 0; pthreads_exited && i < CYGNUM_POSIX_PTHREAD_THREADS_MAX ; i++ ) + { + pthread_info *thread = thread_table[i]; + + if( thread != NULL && thread->state == PTHREAD_STATE_EXITED ) + { + // The thread has exited, so it is a candidate for being + // reaped. We have to make sure that the eCos thread has + // also reached EXITED state before we can tidy it up. + + while( thread->thread->get_state() != Cyg_Thread::EXITED ) + { + // The eCos thread has not yet exited. This is + // probably because its priority is too low to allow + // it to complete. We fix this here by raising its + // priority to equal ours and then yielding. This + // should eventually get it into exited state. + + Cyg_Thread *self = Cyg_Thread::self(); + + // Set thread's priority to our current dispatching priority. + thread->thread->set_priority( self->get_current_priority() ); + + // Yield, yield + self->yield(); + + // and keep looping until he exits. + } + + // At this point we have a thread that we can reap. + + // destroy the eCos thread + thread->thread->~Cyg_Thread(); + + // destroy the joiner condvar + thread->joiner->~Cyg_Condition_Variable(); + + // Destroy signal handling fields + cyg_posix_thread_sigdestroy( thread ); + + // Free the stack if we allocated it + if( thread->freestack ) + pthread_free( thread->stackmem ); + + // Finally, set the thread table entry to NULL so that it + // may be reused. + thread_table[i] = NULL; + + pthread_count--; + pthreads_exited--; + } + } +} + +//============================================================================= +// Functions exported to rest of POSIX subsystem. + +//----------------------------------------------------------------------------- +// Create the main() thread. + +externC void cyg_posix_pthread_start( void ) +{ + + // Initialize the per-thread data key map. + + for( cyg_ucount32 i = 0; i < (PTHREAD_KEYS_MAX/KEY_MAP_TYPE_SIZE); i++ ) + { + thread_key[i] = ~0; + } + + // Create the main thread + pthread_attr_t attr; + struct sched_param schedparam; + + schedparam.sched_priority = CYGNUM_POSIX_MAIN_DEFAULT_PRIORITY; + + pthread_attr_init( &attr ); + pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setstackaddr( &attr, &main_stack[CYGNUM_POSIX_MAIN_DEFAULT_STACK_SIZE] ); + pthread_attr_setstacksize( &attr, CYGNUM_POSIX_MAIN_DEFAULT_STACK_SIZE ); + pthread_attr_setschedpolicy( &attr, SCHED_RR ); + pthread_attr_setschedparam( &attr, &schedparam ); + + pthread_create( &main_thread, &attr, call_main, NULL ); +} + +//----------------------------------------------------------------------------- +// Look for a thread that can accept delivery of any of the signals in +// the mask and release it from any wait it is in. Since this may be +// called from a DSR, it cannot use any locks internally - any locking +// should be done before the call. + +externC void cyg_posix_pthread_release_thread( sigset_t *mask ) +{ + int i; + int count = pthread_count; + + // Loop over the thread table looking for a thread that has a + // signal mask that does not mask all the signals in mask. + // FIXME: find a more efficient way of doing this. + + for( i = 0; count > 0 && i < CYGNUM_POSIX_PTHREAD_THREADS_MAX ; i++ ) + { + pthread_info *thread = thread_table[i]; + + if( (thread != NULL) && + (thread->state == PTHREAD_STATE_RUNNING) && + ((*mask & ~thread->sigmask) != 0) ) + { + // This thread can service at least one of the signals in + // *mask. Knock it out of its wait and make its ASR pending. + + thread->thread->set_asr_pending(); + thread->thread->release(); + break; + } + + if( thread->state != PTHREAD_STATE_FREE ) + count--; + } +} + +//============================================================================= +// General thread operations + +//----------------------------------------------------------------------------- +// Thread creation and management. + +// Create a thread. +externC int pthread_create ( pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine) (void *), + void *arg) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(thread); + PTHREAD_CHECK(start_routine); + + pthread_info *self = pthread_self_info(); + + pthread_attr_t use_attr; + + // Set use_attr to the set of attributes we are going to + // actually use. Either those passed in, or the default set. + + if( attr == NULL ) + pthread_attr_init( &use_attr ); + else use_attr = *attr; + + // Adjust the attributes to cope with the setting of inheritsched. + + if( use_attr.inheritsched == PTHREAD_INHERIT_SCHED ) + { + use_attr.schedpolicy = self->attr.schedpolicy; + use_attr.schedparam = self->attr.schedparam; + } + + CYG_ADDRWORD stackbase, stacksize; + cyg_bool freestack = false; + CYG_ADDRWORD stackmem = 0; + + // If the stack size is not valid, we can assume that it is at + // least PTHREAD_STACK_MIN bytes. + + if( use_attr.stacksize_valid ) + stacksize = use_attr.stacksize; + else stacksize = PTHREAD_STACK_MIN; + + if( use_attr.stackaddr_valid ) + { + // Set up stack base and size from supplied arguments. + + // Calculate stack base from address and size. + // FIXME: Falling stack assumed in pthread_create(). + stackmem = stackbase = (CYG_ADDRWORD)use_attr.stackaddr-stacksize; + } + else + { +#ifdef PTHREAD_MALLOC + + stackmem = stackbase = pthread_malloc( stacksize ); + + if( stackmem == 0 ) + PTHREAD_RETURN( EAGAIN ); + + freestack = true; +#else + PTHREAD_RETURN(EINVAL); +#endif + + } + + // Get sole access to data structures + + pthread_mutex.lock(); + + // Dispose of any dead threads + pthread_reap(); + + // Find a free slot in the thread table + + pthread_info *nthread; + int thread_next = thread_info_next; + + while( thread_table[thread_next] != NULL ) + { + thread_next++; + if( thread_next >= CYGNUM_POSIX_PTHREAD_THREADS_MAX ) + thread_next = 0; + + // check for wrap, and return error if no slots left + if( thread_next == thread_info_next ) + { + pthread_mutex.unlock(); + if( freestack ) + pthread_free( stackmem ); + PTHREAD_RETURN(ENOMEM); + } + } + + nthread = (pthread_info *)stackbase; + + stackbase += sizeof(pthread_info); + stacksize -= sizeof(pthread_info); + + thread_table[thread_next] = nthread; + + // Set new next index + thread_info_next = thread_next; + + // step the cookie + thread_id_cookie += THREAD_ID_COOKIE_INC; + + // Initialize the table entry + nthread->state = use_attr.detachstate == PTHREAD_CREATE_JOINABLE ? + PTHREAD_STATE_RUNNING : PTHREAD_STATE_DETACHED; + nthread->id = thread_next+thread_id_cookie; + nthread->attr = use_attr; + nthread->retval = 0; + nthread->start_routine = start_routine; + nthread->start_arg = arg; + + nthread->freestack = freestack; + nthread->stackmem = stackmem; + + nthread->cancelstate = PTHREAD_CANCEL_ENABLE; + nthread->canceltype = PTHREAD_CANCEL_DEFERRED; + nthread->cancelbuffer = NULL; + nthread->cancelpending = false; + + nthread->thread_data = NULL; + +#ifdef CYGVAR_KERNEL_THREADS_NAME + // generate a name for this thread + + char *name = nthread->name; + static char *name_template = "pthread.00000000"; + pthread_t id = nthread->id; + + for( int i = 0; name_template[i]; i++ ) name[i] = name_template[i]; + + // dump the id, in hex into the name. + for( int i = 15; i >= 8; i-- ) + { + name[i] = "0123456789ABCDEF"[id&0xF]; + id >>= 4; + } + +#endif + + // Initialize the joiner condition variable + + nthread->joiner = new(nthread->joiner_obj) Cyg_Condition_Variable( pthread_mutex ); + + // Initialize signal specific fields. + cyg_posix_thread_siginit( nthread ); + + // create the underlying eCos thread + + nthread->thread = new(&nthread->thread_obj[0]) + Cyg_Thread ( PTHREAD_ECOS_PRIORITY(use_attr.schedparam.sched_priority), + pthread_entry, + (CYG_ADDRWORD)nthread, + name, + stackbase, + stacksize); + + // Put pointer to pthread_info into eCos thread's per-thread data. + nthread->thread->set_data( CYGNUM_KERNEL_THREADS_DATA_POSIX, (CYG_ADDRWORD)nthread ); + + // Set timeslice enable according to scheduling policy. + if( use_attr.schedpolicy == SCHED_FIFO ) + nthread->thread->timeslice_disable(); + else nthread->thread->timeslice_enable(); + + // set up ASR and data + nthread->thread->set_asr( posix_asr, (CYG_ADDRWORD)nthread, NULL, NULL ); + + // return thread ID + *thread = nthread->id; + + pthread_count++; + + pthread_mutex.unlock(); + + // finally, set the thread going + nthread->thread->resume(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get current thread id. + +externC pthread_t pthread_self ( void ) +{ + PTHREAD_ENTRY(); + + pthread_info *info = pthread_self_info(); + + CYG_CHECK_DATA_PTR(info, "Not a POSIX thread!!!"); + + return info->id; +} + +//----------------------------------------------------------------------------- +// Compare two thread identifiers. + +externC int pthread_equal (pthread_t thread1, pthread_t thread2) +{ + PTHREAD_ENTRY(); + + return thread1 == thread2; +} + +//----------------------------------------------------------------------------- +// Terminate current thread. + +externC void exit(int) CYGBLD_ATTRIB_NORET; + +externC void pthread_exit (void *retval) +{ + PTHREAD_ENTRY(); + + pthread_info *self = pthread_self_info(); + + // Call cancellation handlers. We eat up the buffers as we go in + // case any of the routines calls pthread_exit() itself. + while( self->cancelbuffer != NULL ) + { + struct pthread_cleanup_buffer *buffer = self->cancelbuffer; + + self->cancelbuffer = buffer->prev; + + buffer->routine(buffer->arg); + } + + if( self->thread_data != NULL ) + { + // Call per-thread key destructors. + // The specification of this is that we must continue to call the + // destructor functions until all the per-thread data values are NULL or + // we have done it PTHREAD_DESTRUCTOR_ITERATIONS times. + + cyg_bool destructors_called; + int destructor_iterations = 0; + + do + { + destructors_called = false; + + for( cyg_ucount32 key = 0; key < PTHREAD_KEYS_MAX; key++ ) + { + // Skip unallocated keys + if( thread_key[key/KEY_MAP_TYPE_SIZE] & 1<<(key%KEY_MAP_TYPE_SIZE) ) + continue; + + // Skip NULL destructors + if( key_destructor[key] == NULL ) continue; + + // Skip NULL data values + if( self->thread_data[key] == NULL ) continue; + + // If it passes all that, call the destructor + key_destructor[key]((void *)(self->thread_data[key])); + + // Record that we called a destructor + destructors_called = true; + } + + // Count the iteration + destructor_iterations++; + + } while( destructors_called && + (destructor_iterations <= PTHREAD_DESTRUCTOR_ITERATIONS)); + + } + + pthread_mutex.lock(); + + // Set the retval for any joiner + self->retval = retval; + + // If we are already detached, go to EXITED state, otherwise + // go into JOIN state. + + if ( PTHREAD_STATE_DETACHED == self->state ) { + self->state = PTHREAD_STATE_EXITED; + pthreads_exited++; + } else { + self->state = PTHREAD_STATE_JOIN; + pthreads_tobejoined++; + } + + // Kick any waiting joiners + self->joiner->broadcast(); + + cyg_bool call_exit=false; + + // if this is the last thread (other than threads waiting to be joined) + // then we need to call exit() later + if ( pthreads_exited + pthreads_tobejoined == pthread_count ) + call_exit=true; + + pthread_mutex.unlock(); + + // Finally, call the exit function; this will not return. + if ( call_exit ) + ::exit(0); + else + self->thread->exit(); + + // This loop keeps some compilers happy. pthread_exit() is marked + // with the noreturn attribute, and without this they generate a + // call to abort() here in case Cyg_Thread::exit() returns. + + for(;;) continue; +} + +//----------------------------------------------------------------------------- +// Wait for the thread to terminate. If thread_return is not NULL then +// the retval from the thread's call to pthread_exit() is stored at +// *thread_return. + +externC int pthread_join (pthread_t thread, void **thread_return) +{ + PTHREAD_ENTRY(); + + pthread_mutex.lock(); + + // Dispose of any dead threads + pthread_reap(); + + pthread_info *self = pthread_self_info(); + pthread_info *joinee = pthread_info_id( thread ); + + if( joinee == NULL ) + { + pthread_mutex.unlock(); + PTHREAD_RETURN(ESRCH); + } + + if( joinee == self ) + { + pthread_mutex.unlock(); + PTHREAD_RETURN(EDEADLK); + } + + switch ( joinee->state ) + { + case PTHREAD_STATE_RUNNING: + // The thread is still running, we must wait for it. + while( joinee->state == PTHREAD_STATE_RUNNING ) + joinee->joiner->wait(); + + // check that the thread is still joinable + if( joinee->state == PTHREAD_STATE_JOIN ) + break; + + // The thread has become unjoinable while we waited, so we + // fall through to complain. + + case PTHREAD_STATE_FREE: + case PTHREAD_STATE_DETACHED: + case PTHREAD_STATE_EXITED: + // None of these may be joined. + pthread_mutex.unlock(); + PTHREAD_RETURN(EINVAL); + + case PTHREAD_STATE_JOIN: + break; + } + + // here, we know that joinee is a thread that has exited and is + // ready to be joined. + + // Get the retval + + if( thread_return != NULL ) + *thread_return = joinee->retval; + + // set state to exited. + joinee->state = PTHREAD_STATE_EXITED; + pthreads_exited++; + pthreads_tobejoined--; + + // Dispose of any dead threads + pthread_reap(); + + pthread_mutex.unlock(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set the detachstate of the thread to "detached". The thread then does not +// need to be joined and its resources will be freed when it exits. + +externC int pthread_detach (pthread_t thread) +{ + PTHREAD_ENTRY(); + + int ret = 0; + + pthread_mutex.lock(); + + pthread_info *detachee = pthread_info_id( thread ); + + if( detachee == NULL ) + ret = ESRCH; // No such thread + else if( detachee->state == PTHREAD_STATE_DETACHED ) + ret = EINVAL; // Already detached! + else + { + // Set state to detached and kick any joinees to + // make them return. + detachee->state = PTHREAD_STATE_DETACHED; + detachee->joiner->broadcast(); + } + + // Dispose of any dead threads + pthread_reap(); + + pthread_mutex.unlock(); + + PTHREAD_RETURN(ret); +} + + +//----------------------------------------------------------------------------- +// Thread attribute handling. + +//----------------------------------------------------------------------------- +// Initialize attributes object with default attributes: +// detachstate == PTHREAD_CREATE_JOINABLE +// scope == PTHREAD_SCOPE_SYSTEM +// inheritsched == PTHREAD_INHERIT_SCHED +// schedpolicy == SCHED_OTHER +// schedparam == unset +// stackaddr == unset +// stacksize == 0 +// + +externC int pthread_attr_init (pthread_attr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + attr->detachstate = PTHREAD_CREATE_JOINABLE; + attr->scope = PTHREAD_SCOPE_SYSTEM; + attr->inheritsched = PTHREAD_INHERIT_SCHED; + attr->schedpolicy = SCHED_OTHER; + attr->schedparam.sched_priority = 0; + attr->stackaddr_valid = 0; + attr->stackaddr = NULL; + attr->stacksize_valid = 0; + attr->stacksize = 0; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Destroy thread attributes object + +externC int pthread_attr_destroy (pthread_attr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + // Nothing to do here... + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set the detachstate attribute + +externC int pthread_attr_setdetachstate (pthread_attr_t *attr, + int detachstate) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( detachstate == PTHREAD_CREATE_JOINABLE || + detachstate == PTHREAD_CREATE_DETACHED ) + { + attr->detachstate = detachstate; + PTHREAD_RETURN(0); + } + + PTHREAD_RETURN(EINVAL); +} + +//----------------------------------------------------------------------------- +// Get the detachstate attribute +externC int pthread_attr_getdetachstate (const pthread_attr_t *attr, + int *detachstate) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( detachstate != NULL ) + *detachstate = attr->detachstate; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set scheduling contention scope + +externC int pthread_attr_setscope (pthread_attr_t *attr, int scope) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( scope == PTHREAD_SCOPE_SYSTEM || + scope == PTHREAD_SCOPE_PROCESS ) + { + if( scope == PTHREAD_SCOPE_PROCESS ) + PTHREAD_RETURN(ENOTSUP); + + attr->scope = scope; + + PTHREAD_RETURN(0); + } + + PTHREAD_RETURN(EINVAL); +} + +//----------------------------------------------------------------------------- +// Get scheduling contention scope + +externC int pthread_attr_getscope (const pthread_attr_t *attr, int *scope) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( scope != NULL ) + *scope = attr->scope; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set scheduling inheritance attribute + +externC int pthread_attr_setinheritsched (pthread_attr_t *attr, int inherit) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( inherit == PTHREAD_INHERIT_SCHED || + inherit == PTHREAD_EXPLICIT_SCHED ) + { + attr->inheritsched = inherit; + + PTHREAD_RETURN(0); + } + + PTHREAD_RETURN(EINVAL); +} + +//----------------------------------------------------------------------------- +// Get scheduling inheritance attribute + +externC int pthread_attr_getinheritsched (const pthread_attr_t *attr, + int *inherit) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( inherit != NULL ) + *inherit = attr->inheritsched; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set scheduling policy + +externC int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( policy == SCHED_OTHER || + policy == SCHED_FIFO || + policy == SCHED_RR ) + { + attr->schedpolicy = policy; + + PTHREAD_RETURN(0); + } + + PTHREAD_RETURN(EINVAL); +} + +//----------------------------------------------------------------------------- +// Get scheduling policy + +externC int pthread_attr_getschedpolicy (const pthread_attr_t *attr, + int *policy) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( policy != NULL ) + *policy = attr->schedpolicy; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set scheduling parameters +externC int pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + PTHREAD_CHECK(param); + + attr->schedparam = *param; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get scheduling parameters + +externC int pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( param != NULL ) + *param = attr->schedparam; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set starting address of stack. Whether this is at the start or end of +// the memory block allocated for the stack depends on whether the stack +// grows up or down. + +externC int pthread_attr_setstackaddr (pthread_attr_t *attr, void *stackaddr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + attr->stackaddr = stackaddr; + attr->stackaddr_valid = 1; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get any previously set stack address. + +externC int pthread_attr_getstackaddr (const pthread_attr_t *attr, + void **stackaddr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( stackaddr != NULL ) + { + if( attr->stackaddr_valid ) + { + *stackaddr = attr->stackaddr; + PTHREAD_RETURN(0); + } + // Stack address not set, return EINVAL. + else PTHREAD_RETURN(EINVAL); + } + + PTHREAD_RETURN(0); +} + + +//----------------------------------------------------------------------------- +// Set minimum creation stack size. + +externC int pthread_attr_setstacksize (pthread_attr_t *attr, + size_t stacksize) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + CYG_ASSERT( stacksize > PTHREAD_STACK_MIN, "Inadequate stack size supplied"); + + // Reject inadequate stack sizes + if( stacksize < PTHREAD_STACK_MIN ) + PTHREAD_RETURN(EINVAL); + + attr->stacksize_valid = 1; + attr->stacksize = stacksize; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get current minimal stack size. + +externC int pthread_attr_getstacksize (const pthread_attr_t *attr, + size_t *stacksize) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + // Reject attempts to get a stack size when one has not been set. + if( !attr->stacksize_valid ) + PTHREAD_RETURN(EINVAL); + + if( stacksize != NULL ) + *stacksize = attr->stacksize; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Thread scheduling controls + +//----------------------------------------------------------------------------- +// Set scheduling policy and parameters for the thread + +externC int pthread_setschedparam (pthread_t thread_id, + int policy, + const struct sched_param *param) +{ + PTHREAD_ENTRY(); + + if( policy != SCHED_OTHER && + policy != SCHED_FIFO && + policy != SCHED_RR ) + PTHREAD_RETURN(EINVAL); + + PTHREAD_CHECK(param); + + // The parameters seem OK, change the thread... + + pthread_mutex.lock(); + + pthread_info *thread = pthread_info_id( thread_id ); + + if( thread == NULL ) + { + pthread_mutex.unlock(); + PTHREAD_RETURN(ESRCH); + } + + thread->attr.schedpolicy = policy; + thread->attr.schedparam = *param; + + if ( policy == SCHED_FIFO ) + thread->thread->timeslice_disable(); + else thread->thread->timeslice_enable(); + + thread->thread->set_priority( PTHREAD_ECOS_PRIORITY( param->sched_priority )); + + pthread_mutex.unlock(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get scheduling policy and parameters for the thread + +externC int pthread_getschedparam (pthread_t thread_id, + int *policy, + struct sched_param *param) +{ + PTHREAD_ENTRY(); + + pthread_mutex.lock(); + + pthread_info *thread = pthread_info_id( thread_id ); + + if( thread == NULL ) + { + pthread_mutex.unlock(); + PTHREAD_RETURN(ESRCH); + } + + if( policy != NULL ) + *policy = thread->attr.schedpolicy; + + if( param != NULL ) + *param = thread->attr.schedparam; + + pthread_mutex.unlock(); + + PTHREAD_RETURN(0); +} + + +//============================================================================= +// Mutexes + +//----------------------------------------------------------------------------- +// Mutex attributes manipulation functions + +//----------------------------------------------------------------------------- +// Initialize attribute object + +externC int pthread_mutexattr_init ( pthread_mutexattr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + attr->protocol = PTHREAD_PRIO_NONE; + attr->prioceiling = 0; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Destroy attribute object + +externC int pthread_mutexattr_destroy ( pthread_mutexattr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + // Nothing to do here... + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Optional functions depending on priority inversion protection options. + +#if defined(_POSIX_THREAD_PRIO_INHERIT) || defined(_POSIX_THREAD_PRIO_PROTECT) + +// Set priority inversion protection protocol +externC int pthread_mutexattr_setprotocol ( pthread_mutexattr_t *attr, + int protocol) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + switch( protocol ) + { + case PTHREAD_PRIO_NONE: +#if defined(_POSIX_THREAD_PRIO_INHERIT) + case PTHREAD_PRIO_INHERIT: +#endif +#if defined(_POSIX_THREAD_PRIO_PROTECT) + case PTHREAD_PRIO_PROTECT: +#endif + attr->protocol = protocol; + PTHREAD_RETURN(0); + + default: + PTHREAD_RETURN(EINVAL); + } + + PTHREAD_RETURN(0); +} + +// Get priority inversion protection protocol +externC int pthread_mutexattr_getprotocol ( pthread_mutexattr_t *attr, + int *protocol) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( protocol != NULL ) + *protocol = attr->protocol; + + PTHREAD_RETURN(0); +} + +#if defined(_POSIX_THREAD_PRIO_PROTECT) + +// Set priority for priority ceiling protocol +externC int pthread_mutexattr_setprioceiling ( pthread_mutexattr_t *attr, + int prioceiling) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + + attr->prioceiling = prioceiling; + + PTHREAD_RETURN(0); +} + +// Get priority for priority ceiling protocol +externC int pthread_mutexattr_getprioceiling ( pthread_mutexattr_t *attr, + int *prioceiling) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + if( prioceiling != NULL ) + *prioceiling = attr->prioceiling; + + PTHREAD_RETURN(0); +} + +// Set priority ceiling of given mutex, returning old ceiling. +externC int pthread_mutex_setprioceiling( pthread_mutex_t *mutex, + int prioceiling, + int *old_ceiling) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(mutex); + + pthread_mutex_lock( mutex ); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + if( old_ceiling != NULL ) + *old_ceiling = mx->get_ceiling(); + + mx->set_ceiling( prioceiling ); + + pthread_mutex_unlock( mutex ); + + PTHREAD_RETURN(0); +} + +// Get priority ceiling of given mutex +externC int pthread_mutex_getprioceiling( pthread_mutex_t *mutex, + int *prioceiling) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(mutex); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + if( prioceiling != NULL ) + *prioceiling = mx->get_ceiling(); + + PTHREAD_RETURN(0); +} + +#endif // defined(_POSIX_THREAD_PRIO_PROTECT) + +#endif // defined(_POSIX_THREAD_PRIO_INHERIT) || defined(_POSIX_THREAD_PRIO_PROTECT) + +//----------------------------------------------------------------------------- +// Mutex functions + +//----------------------------------------------------------------------------- +// Initialize mutex. If mutex_attr is NULL, use default attributes. + +externC int pthread_mutex_init (pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutex_attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( mutex ); + + pthread_mutexattr_t use_attr; + + // Set up the attributes we are going to use + if( mutex_attr == NULL ) + pthread_mutexattr_init( &use_attr ); + else use_attr = *mutex_attr; + + // Now translate the POSIX protocol identifier into the eCos one. + Cyg_Mutex::cyg_protcol protocol; + + switch( use_attr.protocol ) + { +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING + case PTHREAD_PRIO_PROTECT: + protocol = Cyg_Mutex::CEILING; + break; +#endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT + case PTHREAD_PRIO_INHERIT: + protocol = Cyg_Mutex::INHERIT; + break; +#endif + case PTHREAD_PRIO_NONE: + protocol = Cyg_Mutex::NONE; + break; + + default: + PTHREAD_RETURN(EINVAL); + } + + Cyg_Mutex *mx = new((cyg_uint8 *)mutex) Cyg_Mutex( protocol ); + + if ( protocol == Cyg_Mutex::CEILING ) + mx->set_ceiling( use_attr.prioceiling ); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Destroy mutex. + +externC int pthread_mutex_destroy (pthread_mutex_t *mutex) +{ + PTHREAD_ENTRY(); + + int err = ENOERR; + + PTHREAD_CHECK( mutex ); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + if( mx->get_owner() != NULL ) + err = EBUSY; + else mx->~Cyg_Mutex(); + + PTHREAD_RETURN(err); +} + +//----------------------------------------------------------------------------- +// Lock mutex, waiting for it if necessary. + +externC int pthread_mutex_lock (pthread_mutex_t *mutex) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( mutex ); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + if( mx->get_owner() == Cyg_Thread::self() ) + PTHREAD_RETURN(EDEADLK); + + // Loop here until we acquire the mutex. Even if we are kicked out + // of the wait by a signal or release we must retry. + while( !mx->lock() ) + continue; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Try to lock mutex. + +externC int pthread_mutex_trylock (pthread_mutex_t *mutex) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( mutex ); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + if( mx->get_owner() == Cyg_Thread::self() ) + PTHREAD_RETURN(EDEADLK); + + if( mx->trylock() ) + PTHREAD_RETURN(0); + + PTHREAD_RETURN(EBUSY); +} + + +//----------------------------------------------------------------------------- +// Unlock mutex. + +externC int pthread_mutex_unlock (pthread_mutex_t *mutex) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( mutex ); + + Cyg_Mutex *mx = (Cyg_Mutex *)mutex; + + mx->unlock(); + + PTHREAD_RETURN(0); +} + + +//============================================================================= +// Condition Variables + +//----------------------------------------------------------------------------- +// Attribute manipulation functions +// We do not actually support any attributes at present, so these do nothing. + +//----------------------------------------------------------------------------- +// Initialize condition variable attributes + +externC int pthread_condattr_init (pthread_condattr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + // There are no condition variable attributes at present + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Destroy condition variable attributes + +externC int pthread_condattr_destroy (pthread_condattr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK(attr); + + // nothing to do here... + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Condition variable functions + +//----------------------------------------------------------------------------- +// Initialize condition variable. + +externC int pthread_cond_init (pthread_cond_t *cond, + const pthread_condattr_t *attr) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + + Cyg_Condition_Variable *cv = + new((cyg_uint8 *)cond) Cyg_Condition_Variable(); + + cv = cv; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Destroy condition variable. + +externC int pthread_cond_destroy (pthread_cond_t *cond) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + + ((Cyg_Condition_Variable *)cond)->~Cyg_Condition_Variable(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Wake up one thread waiting for condition variable + +externC int pthread_cond_signal (pthread_cond_t *cond) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + + ((Cyg_Condition_Variable *)cond)->signal(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Wake up all threads waiting for condition variable + +externC int pthread_cond_broadcast (pthread_cond_t *cond) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + + ((Cyg_Condition_Variable *)cond)->broadcast(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Block on condition variable until signalled. The mutex is +// assumed to be locked before this call, will be unlocked +// during the wait, and will be re-locked on wakeup. + +externC int pthread_cond_wait (pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + PTHREAD_CHECK( mutex ); + + ((Cyg_Condition_Variable *)cond)->wait( *(Cyg_Mutex *)mutex ); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Block on condition variable until signalled, or the timeout expires. + +externC int pthread_cond_timedwait (pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( cond ); + PTHREAD_CHECK( mutex ); + PTHREAD_CHECK( abstime ); + + cyg_tick_count ticks; + struct Cyg_Clock::converter ns_converter, sec_converter; + + Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1, &ns_converter ); + Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter ); + + ticks = Cyg_Clock::convert( abstime->tv_sec, &sec_converter ); + ticks += Cyg_Clock::convert( abstime->tv_nsec, &ns_converter ); + + ((Cyg_Condition_Variable *)cond)->wait( *(Cyg_Mutex *)mutex, ticks ); + + PTHREAD_RETURN(0); +} + +//============================================================================= +// Dynamic package initialization +// Call init_routine just the once per control variable. + +externC int pthread_once (pthread_once_t *once_control, + void (*init_routine) (void)) +{ + PTHREAD_ENTRY(); + + PTHREAD_CHECK( once_control ); + PTHREAD_CHECK( init_routine ); + + pthread_once_t old; + + // Do a test and set on the once_control object. + pthread_mutex.lock(); + + old = *once_control; + *once_control = 1; + + pthread_mutex.unlock(); + + // If the once_control was zero, call the init_routine(). + if( !old ) init_routine(); + + PTHREAD_RETURN(0); +} + + +//============================================================================= +//Thread specific data + +//----------------------------------------------------------------------------- +// Create a key to identify a location in the thread specific data area. +// Each thread has its own distinct thread-specific data area but all are +// addressed by the same keys. The destructor function is called whenever a +// thread exits and the value associated with the key is non-NULL. + +externC int pthread_key_create (pthread_key_t *key, + void (*destructor) (void *)) +{ + PTHREAD_ENTRY(); + + pthread_key_t k = -1; + + pthread_mutex.lock(); + + // Find a key to allocate + for( cyg_ucount32 i = 0; i < (PTHREAD_KEYS_MAX/KEY_MAP_TYPE_SIZE); i++ ) + { + if( thread_key[i] != 0 ) + { + // We have a table slot with space available + + // Get index of ls set bit. + HAL_LSBIT_INDEX( k, thread_key[i] ); + + // clear it + thread_key[i] &= ~(1<<k); + + // Add index of word + k += i * KEY_MAP_TYPE_SIZE; + + // Install destructor + key_destructor[k] = destructor; + + // break out with key found + break; + } + } + + if( k != -1 ) + { + // plant a NULL in all the valid thread data slots for this + // key in case we are reusing a key we used before. + + for( cyg_ucount32 i = 0; i < CYGNUM_POSIX_PTHREAD_THREADS_MAX ; i++ ) + { + pthread_info *thread = thread_table[i]; + + if( thread != NULL && thread->thread_data != NULL ) + thread->thread_data[k] = NULL; + } + } + + pthread_mutex.unlock(); + + if( k == -1 ) PTHREAD_RETURN(EAGAIN); + + *key = k; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Delete key. + +externC int pthread_key_delete (pthread_key_t key) +{ + PTHREAD_ENTRY(); + + pthread_mutex.lock(); + + // Set the key bit to 1 to indicate it is free. + thread_key[key/KEY_MAP_TYPE_SIZE] |= 1<<(key%(KEY_MAP_TYPE_SIZE)); + + pthread_mutex.unlock(); + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Store the pointer value in the thread-specific data slot addressed +// by the key. + +externC int pthread_setspecific (pthread_key_t key, const void *pointer) +{ + PTHREAD_ENTRY(); + + if( thread_key[key/KEY_MAP_TYPE_SIZE] & 1<<(key%KEY_MAP_TYPE_SIZE) ) + PTHREAD_RETURN(EINVAL); + + pthread_info *self = pthread_self_info(); + + if( self->thread_data == NULL ) + { + // Allocate the per-thread data table + self->thread_data = + (void **)self->thread->increment_stack_limit( + PTHREAD_KEYS_MAX * sizeof(void *) ); + + // Clear out all entries + for( int i = 0; i < PTHREAD_KEYS_MAX; i++ ) + self->thread_data[i] = NULL; + } + + self->thread_data[key] = (void *)pointer; + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Retrieve the pointer value in the thread-specific data slot addressed +// by the key. + +externC void *pthread_getspecific (pthread_key_t key) +{ + void *val; + PTHREAD_ENTRY(); + + if( thread_key[key/KEY_MAP_TYPE_SIZE] & 1<<(key%KEY_MAP_TYPE_SIZE) ) + PTHREAD_RETURN(NULL); + + pthread_info *self = pthread_self_info(); + + if( self->thread_data == NULL ) + val = NULL; + else val = self->thread_data[key]; + + PTHREAD_RETURN(val); +} + +//============================================================================= +// Thread Cancellation Functions + +//----------------------------------------------------------------------------- +// Set cancel state of current thread to ENABLE or DISABLE. +// Returns old state in *oldstate. + +externC int pthread_setcancelstate (int state, int *oldstate) +{ + PTHREAD_ENTRY(); + + if( state != PTHREAD_CANCEL_ENABLE && + state != PTHREAD_CANCEL_DISABLE ) + PTHREAD_RETURN(EINVAL); + + pthread_mutex.lock(); + + pthread_info *self = pthread_self_info(); + + if( oldstate != NULL ) *oldstate = self->cancelstate; + + self->cancelstate = state; + + pthread_mutex.unlock(); + + // Note: This function may have made it possible for a pending + // cancellation to now be delivered. However the standard does not + // list this function as a cancellation point, so for now we do + // nothing. In future we might call pthread_testcancel() here. + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Set cancel type of current thread to ASYNCHRONOUS or DEFERRED. +// Returns old type in *oldtype. + +externC int pthread_setcanceltype (int type, int *oldtype) +{ + PTHREAD_ENTRY(); + + if( type != PTHREAD_CANCEL_ASYNCHRONOUS && + type != PTHREAD_CANCEL_DEFERRED ) + PTHREAD_RETURN(EINVAL); + + pthread_mutex.lock(); + + pthread_info *self = pthread_self_info(); + + if( oldtype != NULL ) *oldtype = self->canceltype; + + self->canceltype = type; + + pthread_mutex.unlock(); + + // Note: This function may have made it possible for a pending + // cancellation to now be delivered. However the standard does not + // list this function as a cancellation point, so for now we do + // nothing. In future we might call pthread_testcancel() here. + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Cancel the thread. + +externC int pthread_cancel (pthread_t thread) +{ + PTHREAD_ENTRY(); + + pthread_mutex.lock(); + + pthread_info *th = pthread_info_id(thread); + + if( th == NULL ) + { + pthread_mutex.unlock(); + PTHREAD_RETURN(ESRCH); + } + + th->cancelpending = true; + + if( th->cancelstate == PTHREAD_CANCEL_ENABLE && + th->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS ) + { + // If the thread has cancellation enabled, and it is in + // asynchronous mode, set the eCos thread's ASR pending to + // deal with it when the thread wakes up. We also release the + // thread out of any current wait to make it wake up. + + th->thread->set_asr_pending(); + th->thread->release(); + } + // Otherwise the thread either has cancellation disabled, or is + // in deferred mode. In either case it is up to the thread to + // get into a state to deal with the pending cancellation. + + pthread_mutex.unlock(); + + + PTHREAD_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Test for a pending cancellation for the current thread and terminate +// the thread if there is one. + +externC void pthread_testcancel (void) +{ + PTHREAD_ENTRY_VOID(); + + pthread_info *self = pthread_self_info(); + + // Check whether there is a cancel pending and if so, whether + // cancellations are enabled. We do it in this order to reduce the + // number of tests in the common case - when no cancellations are + // pending. + + if( self->cancelpending && + self->cancelstate == PTHREAD_CANCEL_ENABLE ) + + { + // If we have cancellation enabled, and there is a cancellation + // pending, then go ahead and do the deed. + + // Exit now with special retval. pthread_exit() calls the + // cancellation handlers implicitly. + pthread_exit(PTHREAD_CANCELED); + } + + PTHREAD_RETURN_VOID; +} + +//----------------------------------------------------------------------------- +// These two functions actually implement the cleanup push and pop functionality. + +externC void pthread_cleanup_push_inner (struct pthread_cleanup_buffer *buffer, + void (*routine) (void *), + void *arg) +{ + PTHREAD_ENTRY(); + + pthread_info *self = pthread_self_info(); + + buffer->routine = routine; + buffer->arg = arg; + + buffer->prev = self->cancelbuffer; + + self->cancelbuffer = buffer; + + return; +} + +externC void pthread_cleanup_pop_inner (struct pthread_cleanup_buffer *buffer, + int execute) +{ + PTHREAD_ENTRY(); + + pthread_info *self = pthread_self_info(); + + CYG_ASSERT( self->cancelbuffer == buffer, "Stacking error in cleanup buffers"); + + if( self->cancelbuffer == buffer ) + { + // Remove the buffer from the stack + self->cancelbuffer = buffer->prev; + } + else + { + // If the top of the stack is not the buffer we expect, do not + // execute it. + execute = 0; + } + + if( execute ) buffer->routine(buffer->arg); + + return; +} + + +// ------------------------------------------------------------------------- +// EOF pthread.cxx
