/////////////////////////////////////////////////////////////////////////////// // // IMPORTANT NOTICE // // The following open source license statement does not apply to any // entity in the Exception List published by FMSoft. // // For more information, please visit: // // https://www.fmsoft.cn/exception-list // ////////////////////////////////////////////////////////////////////////////// /* * This file is part of MiniGUI, a mature cross-platform windowing * and Graphics User Interface (GUI) support system for embedded systems * and smart IoT devices. * * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. * Copyright (C) 1998~2002, WEI Yongming * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see . * * Or, * * As this program is a library, any link to this program must follow * GNU General Public License version 3 (GPLv3). If you cannot accept * GPLv3, you need to be licensed from FMSoft. * * If you have got a commercial license of this program, please use it * under the terms and conditions of the commercial license. * * For more information about the commercial license, please refer to * . */ /* ** threadx_pthread.c: This file contains the implementation of the POSIX ** pthread functions for ThreadX. ** ** Author: Wei Yongming ** ** Create Date: 2005-01-11 */ #include "mgconfig.h" #if defined (__THREADX__) && defined (_MGUSE_OWN_PTHREAD) #include #include "tx_api.h" #include "common.h" #include "threadx_pprivate.h" //----------------------------------------------------------------------------- // First check that the configuration contains the elements we need //============================================================================= // Internal data structures // Mutex for controlling access to shared data structures TX_MUTEX __txpth_pthread_mutex; // Array of pthread control structures. A pthread_t object is // "just" an index into this array. static pthread_info *thread_table [TXPTHNUM_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 UINT #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 TXPTHNUM_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; //============================================================================= // 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) { TX_THREAD* thread = tx_thread_identify (); pthread_info *info = (pthread_info *)thread; return info; } 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; } //----------------------------------------------------------------------------- // Optional memory allocation functions for pthread stacks. static char byte_pool [TXPTH_SIZE_BYTE_POOL]; static TX_BYTE_POOL bpobj; static void* pthread_malloc (unsigned int size) { UINT ret; void* ptr; ret = tx_byte_allocate (&bpobj, &ptr, size, TX_NO_WAIT); if (ret != TX_SUCCESS) return NULL; return ptr; } static void pthread_free (void* m) { UINT ret; ret = tx_byte_release (m); } //----------------------------------------------------------------------------- // pthread entry function. // does some housekeeping and then calls the user's start routine. static void pthread_entry (ULONG data) { pthread_info *self = (pthread_info *)data; void *retval; retval = self->start_routine (self->start_arg); pthread_exit (retval); } //----------------------------------------------------------------------------- // 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; } #if 0 //----------------------------------------------------------------------------- // POSIX ASR // This is installed as the ASR for all POSIX threads. static void posix_asr (void* data) { pthread_info *self = (pthread_info *)data; // 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); } } #endif //----------------------------------------------------------------------------- // 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 < TXPTHNUM_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 ThreadX thread has // also reached EXITED state before we can tidy it up. while (thread->thread->tx_state != TX_COMPLETED && thread->thread->tx_state != TX_TERMINATED) { // The ThreadX 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. TX_THREAD *self = tx_thread_identify (); UINT old; // Set thread's priority to our current dispatching priority. tx_thread_priority_change (thread->thread, self->tx_priority, &old); // Yield, yield tx_thread_relinquish (); // and keep looping until he exits. } // At this point we have a thread that we can reap. // destroy the ThreadX thread tx_thread_delete (thread->thread); // destroy the joiner event flags tx_event_flags_delete (thread->joiner); // 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. //----------------------------------------------------------------------------- // Main thread. // Thread ID of main thread. static pthread_t main_thread; //----------------------------------------------------------------------------- // Main entry function. // This is set as the start_routine of the main thread. // It invokes the entry function passed by thread argument. struct _main_pth_entry_info { int (* pth_entry) (int argc, const char* argv []); int argc; const char** argv; }; static void *main_pthread_entry (void *data) { struct _main_pth_entry_info* entry_info = (struct _main_pth_entry_info*) data; entry_info->pth_entry (entry_info->argc, entry_info->argv); return NULL; /* placate compiler */ } //----------------------------------------------------------------------------- // Start POSIX Thread system and create the main() thread. int start_minigui_pthread (int (* pth_entry) (int argc, const char* argv []), int argc, const char* argv[], char* stack_base, unsigned int stack_size) { int i; UINT tx_ret; // Initialize the byte pool object tx_ret = tx_byte_pool_create (&bpobj, TXPTH_NAME_BYTE_POOL, byte_pool, TXPTH_SIZE_BYTE_POOL); if (tx_ret != TX_SUCCESS) { TXPTH_FAIL ("PThread: Can not create byte pool object.\n"); return 1; } // Initialize the global mutex object tx_ret = tx_mutex_create (&__txpth_pthread_mutex, TXPTH_NAME_MUTEX, TX_NO_INHERIT); if (tx_ret != TX_SUCCESS) { TXPTH_FAIL ("PThread: Can not create global mutex object.\n"); return 2; } // TODO: Initialize other global object // Initialize the per-thread data key map. for (i = 0; i < (PTHREAD_KEYS_MAX/KEY_MAP_TYPE_SIZE); i++) { thread_key [i] = ~0; } // Create the main thread if (pth_entry) { pthread_attr_t attr; struct sched_param schedparam; struct _main_pth_entry_info entry_info; entry_info.pth_entry = pth_entry; entry_info.argc = argc; entry_info.argv = argv; if (stack_size < MAIN_PTH_MIN_STACK_SIZE) { TXPTH_FAIL ("PThread: Tool small stack size of main thread.\n"); return 3; } schedparam.priority = TXPTH_POSIX_MAIN_DEF_PRIORITY; schedparam.preempt_threshold = TXPTH_POSIX_MAIN_DEF_PREEMPT_THRESHOLD; schedparam.time_slice = TXPTH_POSIX_MAIN_DEF_TIME_SLICE; pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setstackaddr (&attr, stack_base + stack_size); pthread_attr_setstacksize (&attr, stack_size); pthread_attr_setschedpolicy (&attr, SCHED_FIFO); pthread_attr_setschedparam (&attr, &schedparam); pthread_create (&main_thread, &attr, main_pthread_entry, &entry_info); } return 0; } //============================================================================= // General thread operations //----------------------------------------------------------------------------- // Thread creation and management. // Create a thread. int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { UINT tx_ret; char* stackbase; size_t stacksize; BOOL freestack = FALSE; void* stackmem = 0; pthread_info *nthread; int thread_next = thread_info_next; pthread_attr_t use_attr; PTHREAD_ENTRY(); PTHREAD_CHECK (thread); PTHREAD_CHECK (start_routine); // 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) { pthread_info *self = pthread_self_info (); use_attr.schedpolicy = self->attr.schedpolicy; use_attr.schedparam = self->attr.schedparam; } // If the stack size is not valid, we can assume that it is at // least PTHREAD_STACK_DEFAULT bytes. if (use_attr.stacksize_valid) stacksize = use_attr.stacksize; else stacksize = PTHREAD_STACK_DEFAULT; if (use_attr.stackaddr_valid) { // Set up stack base and size from supplied arguments. // Calculate stack base from address and size. stackmem = stackbase = (char*)use_attr.stackaddr - stacksize; } else { stackmem = stackbase = pthread_malloc (stacksize); if (stackmem == 0) PTHREAD_RETURN (EAGAIN); freestack = TRUE; } // Get sole access to data structures tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); // Dispose of any dead threads pthread_reap (); // Find a free slot in the thread table while (thread_table[thread_next] != NULL) { thread_next++; if (thread_next >= TXPTHNUM_POSIX_PTHREAD_THREADS_MAX) thread_next = 0; // check for wrap, and return error if no slots left if (thread_next == thread_info_next) { tx_ret = tx_mutex_put (&__txpth_pthread_mutex); 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; // generate a name for the joiner event flag __txpth_get_name (nthread->joiner_name, NAME_TYPE_EVENTFLAGS, 0); // Initialize the joiner event flag nthread->joiner = &nthread->joiner_obj; tx_ret = tx_event_flags_create (nthread->joiner, nthread->joiner_name); // generate a name for this thread __txpth_get_name (nthread->name, NAME_TYPE_THREAD, 0); nthread->thread = &nthread->thread_obj; // create the underlying ThreadX thread tx_ret = tx_thread_create (nthread->thread, nthread->name, pthread_entry, (ULONG)nthread, stackbase, stacksize, use_attr.schedparam.priority, use_attr.schedparam.preempt_threshold, use_attr.schedparam.time_slice, TX_DONT_START); if (tx_ret != TX_SUCCESS) { tx_mutex_put (&__txpth_pthread_mutex); if (freestack) pthread_free (stackmem); PTHREAD_RETURN (EINVAL); } #if 0 // Put pointer to pthread_info into ThreadX thread's per-thread data. nthread->thread->set_data (TXPTHNUM_KERNEL_THREADS_DATA_POSIX, (void*)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, (void*)nthread, NULL, NULL); #endif // return thread ID *thread = nthread->id; pthread_count++; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); //finally, set the thread going tx_ret = tx_thread_resume (nthread->thread); PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Get current thread id. pthread_t pthread_self (void) { pthread_info* info; PTHREAD_ENTRY(); info = pthread_self_info (); return info->id; } //----------------------------------------------------------------------------- // Compare two thread identifiers. int pthread_equal (pthread_t thread1, pthread_t thread2) { PTHREAD_ENTRY(); return thread1 == thread2; } //----------------------------------------------------------------------------- // Terminate a specific thread. static void txpth_pthread_exit (pthread_info* thread, void *retval) { UINT tx_ret; BOOL call_exit = FALSE; PTHREAD_ENTRY (); // Call cancellation handlers. We eat up the buffers as we go in // case any of the routines calls pthread_exit() itthread. while (thread->cancelbuffer != NULL) { struct pthread_cleanup_buffer *buffer = thread->cancelbuffer; thread->cancelbuffer = buffer->prev; buffer->routine (buffer->arg); } if (thread->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. BOOL destructors_called; int destructor_iterations = 0; do { unsigned int key; destructors_called = FALSE; for (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 (thread->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 = thread->thread_data[key]; thread->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)); } tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); // Set the retval for any joiner thread->retval = retval; // If we are already detached, go to EXITED state, otherwise // go into JOIN state. if (PTHREAD_STATE_DETACHED == thread->state) { thread->state = PTHREAD_STATE_EXITED; pthreads_exited++; } else { thread->state = PTHREAD_STATE_JOIN; pthreads_tobejoined++; } // Kick any waiting joiners tx_event_flags_set (thread->joiner, 0xFFFFFFFF, TX_OR); // 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; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); #if 0 // Finally, call the exit function; this will not return. if (call_exit) exit (0); else tx_thread_terminate (thread->thread); #else tx_thread_terminate (thread->thread); #endif } void pthread_exit (void *retval) { txpth_pthread_exit (pthread_self_info(), retval); } //----------------------------------------------------------------------------- // 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. int pthread_join (pthread_t thread, void **thread_return) { UINT tx_ret; int err = 0; pthread_info* self; pthread_info* joinee; PTHREAD_ENTRY (); // check for cancellation first. pthread_testcancel (); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); // Dispose of any dead threads pthread_reap (); self = pthread_self_info (); joinee = pthread_info_id (thread); if (joinee == NULL) { err = ESRCH; } if (!err && joinee == self) { err = EDEADLK; } if (!err) { switch (joinee->state) { case PTHREAD_STATE_RUNNING: // The thread is still running, we must wait for it. while (joinee->state == PTHREAD_STATE_RUNNING) { #if 0 if (!joinee->joiner->wait()) #endif ULONG actual_flags; tx_mutex_put (&__txpth_pthread_mutex); tx_ret = tx_event_flags_get (joinee->joiner, 0xFFFFFFFF, TX_OR, &actual_flags, TX_WAIT_FOREVER); tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); if (tx_ret != TX_SUCCESS) { // check if we were woken because we were being cancelled if (checkforcancel ()) { err = EAGAIN; // value unimportant, just some error break; } } } // 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. err = EINVAL; break; case PTHREAD_STATE_JOIN: break; } } if (!err) { // 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 (); } tx_ret = tx_mutex_put (&__txpth_pthread_mutex); // check for cancellation before returning pthread_testcancel (); PTHREAD_RETURN (err); } //----------------------------------------------------------------------------- // 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. int pthread_detach (pthread_t thread) { UINT tx_ret; int ret = 0; pthread_info* detachee; PTHREAD_ENTRY(); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); 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; tx_event_flags_set (detachee->joiner, 0xFFFFFFFF, TX_OR); } // Dispose of any dead threads pthread_reap (); tx_mutex_put (&__txpth_pthread_mutex); 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_FIFO // schedparam == unset // stackaddr == unset // stacksize == 0 // 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_FIFO; attr->schedparam.priority = TXPTH_THREAD_DEF_PRIORITY; attr->schedparam.preempt_threshold = TXPTH_THREAD_DEF_PREEMPT_THRESHOLD; attr->schedparam.time_slice = TXPTH_THREAD_DEF_TIME_SLICE; attr->stackaddr_valid = 0; attr->stackaddr = NULL; attr->stacksize_valid = 0; attr->stacksize = 0; PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Destroy thread attributes object int pthread_attr_destroy (pthread_attr_t *attr) { PTHREAD_ENTRY (); PTHREAD_CHECK (attr); // Nothing to do here... PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Set the detachstate attribute 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 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 int pthread_attr_setscope (pthread_attr_t *attr, int scope) { PTHREAD_ENTRY (); PTHREAD_CHECK (attr); if (scope == PTHREAD_SCOPE_SYSTEM) { attr->scope = scope; PTHREAD_RETURN (0); } else if (scope == PTHREAD_SCOPE_PROCESS) { PTHREAD_RETURN (ENOTSUP); } PTHREAD_RETURN (EINVAL); } //----------------------------------------------------------------------------- // Get scheduling contention scope 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 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 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 int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy) { PTHREAD_ENTRY (); PTHREAD_CHECK (attr); if (policy == SCHED_FIFO) { attr->schedpolicy = policy; PTHREAD_RETURN (0); } else if (policy == SCHED_OTHER || policy == SCHED_RR) { PTHREAD_RETURN (ENOTSUP); } PTHREAD_RETURN (EINVAL); } //----------------------------------------------------------------------------- // Get scheduling policy 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 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 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. 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. 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. int pthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize) { PTHREAD_ENTRY (); PTHREAD_CHECK (attr); // 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. 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 int pthread_setschedparam (pthread_t thread_id, int policy, const struct sched_param *param) { UINT tx_ret, tmp; ULONG old_ts; pthread_info *thread; 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... tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); thread = pthread_info_id (thread_id); if (thread == NULL) { tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (ESRCH); } thread->attr.schedpolicy = policy; thread->attr.schedparam = *param; tx_ret = tx_thread_priority_change (thread->thread, param->priority, &tmp); tx_ret = tx_thread_preemption_change (thread->thread, param->preempt_threshold, &tmp); tx_ret = tx_thread_time_slice_change (thread->thread, param->time_slice, &old_ts); tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Get scheduling policy and parameters for the thread int pthread_getschedparam (pthread_t thread_id, int *policy, struct sched_param *param) { UINT tx_ret; pthread_info *thread; PTHREAD_ENTRY (); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); thread = pthread_info_id (thread_id); if (thread == NULL) { tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (ESRCH); } if (policy != NULL) *policy = thread->attr.schedpolicy; if (param != NULL) *param = thread->attr.schedparam; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (0); } //============================================================================= // Dynamic package initialization // Call init_routine just the once per control variable. int pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) { UINT tx_ret; pthread_once_t old; PTHREAD_ENTRY (); PTHREAD_CHECK (once_control); PTHREAD_CHECK (init_routine); // Do a test and set on the once_control object. tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); old = *once_control; *once_control = 1; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); // If the once_control was zero, call the init_routine(). if (!old) init_routine (); PTHREAD_RETURN (0); } //============================================================================= //Thread specific data #define _LSBIT_INDEX(index, mask) index = _lsbit_index (mask) static int _lsbit_index (int mask) { int i; for (i = 0; i < 32; i++) { if (mask & (1<thread_data != NULL ) thread->thread_data[k] = NULL; } } tx_ret = tx_mutex_put (&__txpth_pthread_mutex); if (k == -1) PTHREAD_RETURN (EAGAIN); *key = k; PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Delete key. int pthread_key_delete (pthread_key_t key) { UINT tx_ret; PTHREAD_ENTRY (); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); // Set the key bit to 1 to indicate it is free. thread_key [key/KEY_MAP_TYPE_SIZE] |= 1<<(key%(KEY_MAP_TYPE_SIZE)); tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Store the pointer value in the thread-specific data slot addressed // by the key. int pthread_setspecific (pthread_key_t key, const void *pointer) { pthread_info *self; PTHREAD_ENTRY (); if (thread_key [key/KEY_MAP_TYPE_SIZE] & 1<<(key%KEY_MAP_TYPE_SIZE)) PTHREAD_RETURN (EINVAL); self = pthread_self_info (); if (self->thread_data == NULL) { int i; int size_key_values = PTHREAD_KEYS_MAX * sizeof(void *); // Allocate the per-thread data table self->thread_data = (void **)(self->stackmem + sizeof(pthread_info)); // FIXME: Does need to lock the kernel? (char*)self->thread->tx_stack_start += size_key_values; self->thread->tx_stack_size -= size_key_values; // Clear out all entries for (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. void *pthread_getspecific (pthread_key_t key) { void *val; pthread_info *self; PTHREAD_ENTRY (); if (thread_key [key/KEY_MAP_TYPE_SIZE] & 1<<(key%KEY_MAP_TYPE_SIZE)) PTHREAD_RETURN(NULL); 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. int pthread_setcancelstate (int state, int *oldstate) { UINT tx_ret; pthread_info *self; PTHREAD_ENTRY (); if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) PTHREAD_RETURN (EINVAL); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); self = pthread_self_info (); if (oldstate != NULL) *oldstate = self->cancelstate; self->cancelstate = state; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); // 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. int pthread_setcanceltype (int type, int *oldtype) { UINT tx_ret; pthread_info *self; PTHREAD_ENTRY (); if (type != PTHREAD_CANCEL_ASYNCHRONOUS && type != PTHREAD_CANCEL_DEFERRED ) PTHREAD_RETURN (EINVAL); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); self = pthread_self_info (); if (oldtype != NULL) *oldtype = self->canceltype; self->canceltype = type; tx_ret = tx_mutex_put (&__txpth_pthread_mutex); // 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. int pthread_cancel (pthread_t thread) { UINT tx_ret; pthread_info *th; PTHREAD_ENTRY (); tx_ret = tx_mutex_get (&__txpth_pthread_mutex, TX_WAIT_FOREVER); th = pthread_info_id (thread); if (th == NULL) { tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (ESRCH); } th->cancelpending = TRUE; if (th->cancelstate == PTHREAD_CANCEL_ENABLE) { if (th->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) { // If the thread has cancellation enabled, and it is in // asynchronous mode, call txpth_pthread_exit directly. txpth_pthread_exit (th, PTHREAD_CANCELED); } else if (th->canceltype == PTHREAD_CANCEL_DEFERRED) { // If the thread has cancellation enabled, and it is in // deferred mode, wake the thread up so that cancellation // points can test for cancellation. tx_thread_wait_abort (th->thread); } else TXPTH_FAIL ("Unknown cancellation type"); } // Otherwise the thread has cancellation disabled, in which case // it is up to the thread to enable cancellation tx_ret = tx_mutex_put (&__txpth_pthread_mutex); PTHREAD_RETURN (0); } //----------------------------------------------------------------------------- // Test for a pending cancellation for the current thread and terminate // the thread if there is one. void pthread_testcancel (void) { PTHREAD_ENTRY_VOID (); if (checkforcancel ()) { // 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. void pthread_cleanup_push_inner (struct pthread_cleanup_buffer *buffer, void (*routine) (void *), void *arg) { pthread_info *self; PTHREAD_ENTRY (); self = pthread_self_info (); buffer->routine = routine; buffer->arg = arg; buffer->prev = self->cancelbuffer; self->cancelbuffer = buffer; return; } void pthread_cleanup_pop_inner (struct pthread_cleanup_buffer *buffer, int execute) { pthread_info *self; PTHREAD_ENTRY (); self = pthread_self_info (); TXPTH_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; } #endif /* __THREADX__ && _MGUSE_OWN_PTHREAD */