📄 pthread.cxx
字号:
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;
}
// Decrement count for each valid thread we find.
if( thread != NULL && thread->state != PTHREAD_STATE_FREE )
count--;
}
}
#endif
//=============================================================================
// 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 )
{
CYG_ASSERT( NULL != self,
"Attempt to inherit sched policy from non-POSIX thread" );
#ifdef CYGDBG_USE_ASSERTS
// paranoia check
int i;
for (i=(sizeof(thread_table)/sizeof(*thread_table))-1; i>=0; i--) {
if (thread_table[i] == self)
break;
}
CYG_ASSERT( i>=0, "Current pthread not found in table" );
#endif
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 );
#ifdef CYGPKG_POSIX_SIGNALS
// Initialize signal specific fields.
if (NULL != self) {
CYG_CHECK_DATA_PTR( self,
"Attempt to inherit signal mask from bogus pthread" );
#ifdef CYGDBG_USE_ASSERTS
// paranoia check
int i;
for (i=(sizeof(thread_table)/sizeof(*thread_table))-1; i>=0; i--) {
if (thread_table[i] == self)
break;
}
CYG_ASSERT( i>=0, "Current pthread not found in table" );
#endif
}
cyg_posix_thread_siginit( nthread, self );
#endif
// 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();
// Disable cancellation requests for this thread. If cleanup
// handlers exist, they will generally be issuing system calls
// to clean up resources. We want these system calls to run
// without cancelling, and we also want to prevent being
// re-cancelled.
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
// 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.
// Note that NULLing the data value here is new
// behaviour in the 2001 POSIX standard.
{
void* value = self->thread_data[key];
self->thread_data[key] = NULL;
key_destructor[key](value);
}
// 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)
{
int err = 0;
PTHREAD_ENTRY();
// check for cancellation first.
pthread_testcancel();
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 )
{
err = ESRCH;
}
if( !err && joinee == self )
{
err = EDEADLK;
}
if ( !err ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -