📄 pthread.cxx
字号:
//==========================================================================
//
// pthread.cxx
//
// POSIX pthreads implementation
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos 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 General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####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 <pkgconf/libc_startup.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/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 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.
#define MAIN_DEFAULT_STACK_SIZE \ (CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE < PTHREAD_STACK_MIN \ ? PTHREAD_STACK_MIN : CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE)
static char main_stack[MAIN_DEFAULT_STACK_SIZE];
// Thread ID of main thread.
static pthread_t main_thread;
//=============================================================================
// Exported variables
int pthread_canceled_dummy_var; // 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);
// This assertion mustn't be enabled because sometimes we can legitimately
// carefully call this as long as we realise the value can be NULL.
// e.g. consider the use of this when inheriting sigmasks when in the
// context of creating the main() thread.
// 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
}
//-----------------------------------------------------------------------------
// 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.
// We make this inline so it can be called directly below for speed
static __inline__ int
checkforcancel( void )
{
pthread_info *self = pthread_self_info();
if( self != NULL &&
self->cancelpending &&
self->cancelstate == PTHREAD_CANCEL_ENABLE )
return 1;
else
return 0;
}
//-----------------------------------------------------------------------------
// 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;
#ifdef CYGPKG_POSIX_TIMERS
// Call into timer subsystem to deliver any pending
// timer expirations.
cyg_posix_timer_asr(self);
#endif
#ifdef CYGPKG_POSIX_SIGNALS
// Call signal subsystem to deliver any signals
cyg_posix_signal_asr(self);
#endif
// 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 them 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();
#ifdef CYGPKG_POSIX_SIGNALS
// Destroy signal handling fields
cyg_posix_thread_sigdestroy( thread );
#endif
// 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[sizeof(main_stack)] );
pthread_attr_setstacksize( &attr, sizeof(main_stack) );
pthread_attr_setschedpolicy( &attr, SCHED_RR );
pthread_attr_setschedparam( &attr, &schedparam );
pthread_create( &main_thread, &attr, call_main, NULL );
}
#ifdef CYGPKG_POSIX_SIGNALS
//-----------------------------------------------------------------------------
// 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 )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -