/* pthreadLib.c - POSIX 1003.1 thread library interfaces */ /* * Copyright (c) 2000-2013, 2015, 2017 Wind River Systems, Inc. * * The right to copy, distribute, modify or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* modification history -------------------- 07feb17,dlk address a new static analysis warning (VXW6-86169) 31jan17,dlk address static analysis warnings (VXW6-86155) 18sep15,h_k fixed pthread_mutex_setprioceiling() and pthread_mutex_unlock() clear errno. (VXW6-21587, VXW6-21618) 05nov13,to suppress a false-positive static analysis issue 04nov13,dee fix static analysis issues 13sep13,jdw Fix Coverity issues (WIND00433558) 03dec11,jpb Removed active wait and replace with taskWait. 11may12,jpb Defect WIND00347614. Fixed warnings. 16mar12,pad Fixed library's and pthread_mutexattr_setprotocol()' manual. 22feb12,pad Allow pthread_kill() to be called by a native VxWorks task (defect 333144) and do the same for pthread_getschedparam() and pthread_setschedparam(). 26jul11,pad Kept only Wind River copyright (defect 288940). 17jan11,pad Made so that a pthread cancelled while holding another pthread's lock will always release the lock prior to exiting (defect 242878). Also: synchronized most of the code between pthread_exit() and pthread_abnormal_exit() via the use of an inline routine, removed obsolete AE-related logic and other miscellaneous enhancements. 08dec10,zl updated semXCreate() verification to use typed macro 11nov10,pad For improved performances replaced PTHREAD_SELF_TO_INTERNALPTHREAD() macro with _pthread_id_self() inline routine. Various code cleanups. 02nov10,pad Performance improvements to pthread_testandset_canceltype(), and pthread_canceltype() as discussed in the defect 233318. Also improved performances pthread_setcancelstate(). 01jul10,jpb Replaced exit() with taskExit(). 14apr10,jpb Updated for LP64 adaptation. 12feb10,kk modified pthread_cond_timedwait() to use TV_CONVERT_TO_TICK_NO_PARTIAL() (WIND00186253) 16dec09,gls fixed condition variable/signal interaction (WIND00193764) 30sep09,jpb Defect #184564. pthread_abnormal_exit should just return instead of calling exit() in the case where PTHREAD_VERIFY_AND_LOCK fails. 20apr09,cww use fixed size atomic operators 27aug08,jpb Renamed VSB header file 02sep08,pad Now the synchronization semaphore used for joining the thread is properly deleted even when the thread sets itself in detach mode (defect 130517). 22aug08,pad Restore proper priority when the guard mutex associated to a condition variable is using the PTHREAD_PRIO_PROTECT protocol (defect 130350). pthread_cond_timed_wait() now decrements the guard mutex's reference count when the function times out (defect 130210). 18jun08,jpb Renamed _WRS_VX_SMP to _WRS_CONFIG_SMP. Added include path for kernel configurations options set in vsb. 15apr08,jpb Defect #112751. Fixed return value check for VALID_PTHREAD. 08apr08,jpb Defect #119383. Edited pthread_setspecific to allow for the value of the key to be NULL. 21aug07,pad Adjusted the design of the cancellation feature so that it works for pthread_cond_wait() and pthread_cond_timedwait() now that they use the semExchange() API (required for SMP) (defect #102076). Also fixed a bug in the synchronous path of the cancellation. 08aug07,jpb Removed task locks from pthread_cond_wait and replaced it with semExchange. Kept the original code for UP. Ported user-side changes of pthread_once but kept in original code for UP. 02may07,jpb SMP audit. Added a locking mechanism that will lock the thread and verify its validity; keeps it locked until critical section work is finished. Removed tasklocks in MUTEX_INIT_COMPLETE and COND_INIT_COMPLETE macros and replaced it with using a global semaphore (user-side algorithm). Removed CANCEL_LOCKS as they are not needed anymore. Renamed pthreadDeleteTask to pthread_abnormal_exit. Moved PTHREAD_LOCK, PTHREAD_UNLOCK, and MY_PTHREAD macros to private header file. 07nov06,jpb Added support to changing the priority if the scheduler is SCHED_OTHER (defect #70991) 29sep06,pad Make sure that pthread_join() does not use null task names in string comparisons (defect #67271 / fix #67273). 22sep06,pad Prevented a race condition to occur when a priority ceiling mutex is released (defect #66491 / fix #66536). 11sep06,pad Fixed implementation of pthread_cond_broadcast() (defect #64617 / fix #64618). 10aug06,pad Adapted to member name change in _Sched_param structure. 02aug06,pad Adapted to new definition of pthread_attr_t structure. Re-instated taskIdCurrent in mutex lock/unlock routines for performance reasons. 26jul06,pad Completed proper support for the PTHREAD_PRIO_PROTECT protocol. Replaced taskIdCurrent with taskIdSelf() for SMP's sake. 13apr06,pad Library's documentation update. 03apr06,dgp doc: fix formatting of SEE ALSO in pthread_cond_destroy( ), pthread_cond_init( ), pthread_cond_timedwait( ), and pthread_condattr_init( ) 08mar06,jpb Updating various SPR's to reflect user side changes SPR#118167: too late condition in pthread_cond_timedoutwait() SPR#117769: check range in pthread_mutex_setprioceiling SPR#114130: if cancelstate is enabled and if canceltype is asynchronous, kill immediately otherwise set to pending. SPR#114293: Created keyStatus in pthread_key SPR#114360: pthread_mutexattr_init() attribute structure initialization SPR#117697: added check for pending cancellation requests SPR#115263: Now pthread_exit() sets the cancelability state to disabled and the cancelability type to deferred (). 15nov05,rp fixed pthread_mutex_unlock to check for ownership before changing task priority (SPR 115119) 21sep05,pad Fixed incorrect information in documentation (SPR 112541). 27aug05,pad Updated documentation. Changed a few routine's manuals and error codes for coherency with user-side pthreadLib. 01mar05,job cleanupPrivateData() was incorrectly freeing the global key data structure, as well as the per-thread copy. This should only be done by a call to pthread_key_delete(). SPR#106515. Updated docs to pthread_key_create() and pthread_key_delete(). 06jan05,pad Added call to taskIdVerify() in VALID_PTHREAD macro so that a deleted thread is not accidentally considered valid when the memory previously allocated to the internalPthread structure is recycled (SPR #105025). 01oct04,pad pthread_join() now exits only when the VxWorks task behind the thread is actually gone (SPR #101604). Fixed a potential race condition between pthread_join() and pthread_exit(). 25sep04,pad Fixed a hole in the uniqueness of execution of the routine called via pthread_once() (SPR #98589). Fixed incorrect handling of initialized unlocked mutex in pthread_mutex_destroy (SPR #101108). 21sep04,pad pthread_attr_setstacksize() now returns EINVAL if the stack size is lower than PTHREAD_STACK_MIN. The documentation has been updated to match the user-side pthreadLib's documentation. Fixed a race condition between thread_join() and pthread_exit(). 16jun04,pad Updated documentation regarding cancellation points. 09jun04,dgp clean up formatting to correct doc build errors, convert to apigen markup 04jun04,pad Added pthread_attr_setopt() and pthread_attr_getopt() to set and get the task options for the VxWorks task under the thread, and adapted pthread_attr_init() accordingly (SPR 92417). 12may04,pad Updated wrapperFunc()'s interface to pass caller's signal mask (SPR 95215). Fixed pthread_mutex_destroy(). 27feb04,pad Reset condValid to false in pthread_cond_destroy() (SPR# 82967). 10dec03,pad Now pthread_exit() sets the TASK_EXITED state of detached threads before starting the cleanup work (SPR 92453). 05dec03,pad Now pthread_join() no longer generates an exception when its status parameter is NULL (SPR #92320). 13jan03,gls made pthread_cond_wait() do atomic release/wait (SPR #85235) merged changes to INIT_MUTEX/INIT_COND from Dot-4 (SPR #85312) added check for waiting thread in pthread_cond_signal (SPR #85264). 03may02,gls updated pthread_attr_setstackaddr documentation (SPR #76769) 22apr02,gls removed references to AE (SPR #75799) 05nov01,gls merged in code from AE 24oct01,jgn correct scheduling policy inheritance (SPR #71125) (docs update - main change in _pthreadLib.c) 04apr01,pfl doc update (SPR #63976) 11sep00,jgn split into user and system level components (SPR #33375) 22aug00,jgn remove dependency on TCB spare4 field (SPR #33815) + fix some memory handling & memory leaks (SPR #33554) + move readdir_r() to dirLib.c (SPR #34056) 15aug00,jgn add bug fixes from DOT-4 17jul00,jgn created from DOT-4 version 1.17 */ /* DESCRIPTION This library provides an implementation of POSIX 1003.1c threads for VxWorks. This provides an increased level of compatibility between VxWorks applications and those written for other operating systems that support the POSIX threads model (often called ). VxWorks implements POSIX threads in the kernel based on tasks. Because the kernel environment is different from a process environment, in the POSIX sense, there are a few restrictions in the implementation, but in general, since tasks are roughly equivalent to threads, the support maps well onto VxWorks. The restrictions are explained in more detail in the following paragraphs. SYSTEM CALL HANDLERS CONSIDERATIONS None of the pthread API may be invoked in the context of a system call handler, either by being called directly in the system call handler or by being called by any routine that the system call handler may use. Not conforming to this restriction will lead to unspecified behavior and failure. CONFIGURATION To add POSIX threads support to a system, the component INCLUDE_POSIX_PTHREADS must be added. THREADS A thread is essentially a VxWorks task, with some additional characteristics. The first is detachability, where the creator of a thread can optionally block until the thread exits. The second is cancelability, where one task or thread can cause a thread to exit, possibly calling cleanup handlers. The next is private data, where data private to a thread is created, accessed and deleted via keys. Each thread has a unique ID. A thread's ID is different than it's VxWorks task ID. It is recommended to use the POSIX thread API only via POSIX threads, not via native VxWorks tasks. Since pthreads are not created by default in VxWorks the pthread_create() API can be safely used by a native VxWorks task in order to create the first POSIX thread. If a native VxWorks task must use more pthread API it is recommended to give this task a pthread persona by calling pthread_self() first. Once a native VxWorks task has called pthread_self() it is recommended that it also uses the pthread_exit() API to exit. Note that a native VxWorks task that has been given a POSIX thread persona differs from a regular POSIX thread (i.e. created by the pthread_create() API) in that it cannot be joined (i.e. it is as if it were created with the PTHREAD_CREATE_DETACHED state). The reason being that there is no practical way for another POSIX thread to join the newly formed pthread. MUTEXES Included with the POSIX threads facility is a mutual exclusion facility, or . These are functionally similar to the VxWorks mutex semaphores (see 'semMLib' for more detail), and in fact are implemented using a VxWorks mutex semaphore. The advantage they offer, like all of the POSIX libraries, is the ability to run software designed for POSIX platforms under VxWorks. There are three types of locking protocols available: PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT. PTHREAD_PRIO_INHERIT is the default and maps to a semaphore created with SEM_Q_PRIORITY and SEM_INVERSION_SAFE set (see 'semMCreate' for more detail). A thread locking a mutex created with its protocol attribute set to PTHREAD_PRIO_PROTECT has its priority elevated to that of of the prioceiling attribute of the mutex. When the mutex is unlocked, the priority of the calling thread is restored to its previous value. Both protocols aim at solving the priority inversion problem where a lower priority thread can unduly delay a higher priority thread requiring the resource blocked by the lower priority thread. The PTHREAD_PRIO_INHERIT protocol can be more efficient since it elevates the priority of a thread only when needed. The PTHREAD_PRIO_PROTECT protocol gives more control over the priority change at the cost of systematically elevating the thread's priority as well as preventing threads to use a mutex which priority ceiling is lower than the thread's priority. In contrast the PTHREAD_PRIO_NONE protocol does not affect the priority and scheduling of the thread that owns the mutex. CONDITION VARIABLES Condition variables are another synchronization mechanism that is included in the POSIX threads library. A condition variable allows threads to block until some condition is met. There are really only two basic operations that a condition variable can be involved in: waiting and signalling. Condition variables are always associated with a mutex. A thread can wait for a condition to become true by taking the mutex and then calling pthread_cond_wait(). That function will release the mutex and wait for the condition to be signalled by another thread. When the condition is signalled, the function will re-acquire the mutex and return to the caller. Condition variable support two types of signalling: single thread wake-up using pthread_cond_signal(), and multiple thread wake-up using pthread_cond_broadcast(). The latter of these will unblock all threads that were waiting on the specified condition variable. It should be noted that condition variable signals are not related to POSIX signals. In fact, they are implemented using VxWorks semaphores. RESOURCE COMPETITION All tasks, and therefore all POSIX threads, compete for CPU time together. For that reason the contention scope thread attribute is always 'PTHREAD_SCOPE_SYSTEM'. NO VXWORKS EQUIVALENT Since there is no notion of a process (in the POSIX sense) in the kernel environment, there is no notion of sharing of locks (mutexes) and condition variables between processes. As a result, the POSIX symbol '_POSIX_THREAD_PROCESS_SHARED' is not defined in this implementation, and the routines pthread_condattr_getpshared(), pthread_condattr_setpshared(), pthread_mutexattr_getpshared() are not implemented. Also, since the VxWorks kernel is not a process environment, fork(), wait(), and pthread_atfork() are unimplemented. SCHEDULING The default scheduling policy for a created thread is inherited from the system setting at the time of creation. Unlike for the pthread support in RTPs, the POSIX threads in the kernel are not scheduled by the POSIX scheduler. They are scheduled by the VxWorks native scheduler, like all other VxWorks tasks: scheduling policies under VxWorks are global; they are not set per-thread, as the POSIX model describes. As a result, the scheduling routines, as well as the POSIX scheduling routines native to VxWorks, do not allow you to change the scheduling policy for kernel pthreads. Under VxWorks you may set the scheduling policy in a thread, but if it does not match the system's scheduling policy, an error is returned. The detailed explanation for why this situation occurs is a bit convoluted: technically the scheduling policy is an attribute of a thread (in that there are pthread_attr_getschedpolicy() and pthread_attr_setschedpolicy() functions that define what the thread's scheduling policy will be once it is created, and not what any thread should do at the time they are called). A situation arises where the scheduling policy in force at the time of a thread's creation is not the same as set in its attributes. In this case pthread_create() fails with the error 'EPERM'. The bottom line is that under VxWorks, if you wish to specify the scheduling policy of a kernel thread, you must set the desired global scheduling policy to match. Kernel threads must then adhere to that scheduling policy, or use the PTHREAD_INHERIT_SCHED mode to inherit the current mode and creator's priority. Alternatively, you can also use pthreads in an RTP. In the kernel, the POSIX scheduling policies are therefore mapped as follows: \is \i SCHED_FIFO is mapped on VxWorks' preemptive priority scheduling. \i SCHED_RR is mapped on VxWorks' round-robin scheduling. \i SCHED_OTHER is mapped on the active VxWorks scheduling policy (either preemptive priority scheduling or round-robin scheduling). This is the only meaningful scheduling policy for kernel pthreads. \ie CREATION AND CANCELLATION Each time a thread is created, the library allocates resources on behalf of it. Each time a VxWorks task (i.e. one not created by the pthread_create() function) uses a POSIX threads feature such as thread private data or pushes a cleanup handler, the library creates resources on behalf of that task as well. Asynchronous thread cancellation is accomplished by way of a signal. A special signal, SIGCNCL, has been set aside in this version of VxWorks for this purpose. Applications should take care not to block or handle SIGCNCL. Current cancellation points in system and library calls: \ts 'Libraries' | 'cancellation points' ---------------------------------- aioPxLib | aio_suspend ioLib | creat, open, read, write, close, fsync, fdatasync, fcntl mqPxLib | mq_receive, mq_send pthreadLib | pthread_cond_timedwait, pthread_cond_wait, pthread_join, pthread_testcancel semPxLib | sem_wait sigLib | pause, sigsuspend, sigtimedwait, sigwait, sigwaitinfo, waitpid timerLib | sleep, nanosleep \te Caveat: due to the implementation of some of the I/O drivers in VxWorks, it is possible that a thread cancellation request can not actually be honored. SUMMARY MATRIX \ts ' function' | 'Implemented?' | 'Note(s)' ------------------------------------------------- pthread_attr_destroy() | Yes | pthread_attr_getdetachstate() | Yes | pthread_attr_getinheritsched() | Yes | pthread_attr_getname() | Yes | 6 pthread_attr_getopt() | Yes | 6 pthread_attr_getschedparam() | Yes | pthread_attr_getschedpolicy() | Yes | pthread_attr_getscope() | Yes | pthread_attr_getstackaddr() | Yes | pthread_attr_getstacksize() | Yes | pthread_attr_init() | Yes | pthread_attr_setdetachstate() | Yes | pthread_attr_setinheritsched() | Yes | pthread_attr_setname() | Yes | 6 pthread_attr_setopt() | Yes | 6 pthread_attr_setschedparam() | Yes | pthread_attr_setschedpolicy() | Yes | pthread_attr_setscope() | Yes | 2 pthread_attr_setstackaddr() | Yes | pthread_attr_setstacksize() | Yes | pthread_atfork() | No | 1 pthread_cancel() | Yes | 5 pthread_cleanup_pop() | Yes | pthread_cleanup_push() | Yes | pthread_condattr_destroy() | Yes | pthread_condattr_getpshared() | No | 3 pthread_condattr_init() | Yes | pthread_condattr_setpshared() | No | 3 pthread_cond_broadcast() | Yes | pthread_cond_destroy() | Yes | pthread_cond_init() | Yes | pthread_cond_signal() | Yes | pthread_cond_timedwait() | Yes | pthread_cond_wait() | Yes | pthread_create() | Yes | pthread_detach() | Yes | pthread_equal() | Yes | pthread_exit() | Yes | pthread_getschedparam() | Yes | 4 pthread_getspecific() | Yes | pthread_join() | Yes | pthread_key_create() | Yes | pthread_key_delete() | Yes | pthread_kill() | Yes | pthread_once() | Yes | pthread_self() | Yes | pthread_setcancelstate() | Yes | pthread_setcanceltype() | Yes | pthread_setschedparam() | Yes | 4 pthread_setspecific() | Yes | pthread_sigmask() | Yes | pthread_testcancel() | Yes | pthread_mutexattr_destroy() | Yes | pthread_mutexattr_getprioceiling() | Yes | pthread_mutexattr_getprotocol() | Yes | pthread_mutexattr_getpshared() | No | 3 pthread_mutexattr_init() | Yes | pthread_mutexattr_setprioceiling() | Yes | pthread_mutexattr_setprotocol() | Yes | pthread_mutexattr_setpshared() | No | 3 pthread_mutex_destroy() | Yes | pthread_mutex_getprioceiling() | Yes | pthread_mutex_init() | Yes | pthread_mutex_lock() | Yes | pthread_mutex_setprioceiling() | Yes | pthread_mutex_trylock() | Yes | pthread_mutex_unlock() | Yes | \te NOTES \ml \m 1 The pthread_atfork() function is not implemented since fork() is not implemented in VxWorks. \m 2 The contention scope thread scheduling attribute is always PTHREAD_SCOPE_SYSTEM, since threads (i.e. tasks) contend for resources with all other threads in the system. \m 3 The routines pthread_condattr_getpshared(), pthread_attr_setpshared(), pthread_mutexattr_getpshared() and pthread_mutexattr_setpshared() are not supported, since these interfaces describe how condition variables and mutexes relate to a process, and the VxWorks kernel is not a process environment. \m 4 The default scheduling policy is inherited from the current system setting. The POSIX model of per-thread scheduling policies is not supported, since a basic tenet of the design of VxWorks is a system-wide scheduling policy. \m 5 Thread cancellation is supported in appropriate routines and those routines already supported by VxWorks. However, the complete list of cancellation points specified by POSIX is not supported because routines such as msync(), tcdrain(), and wait() are not implemented by VxWorks. \m 6 VxWorks-specific routines provided as an extension to IEEE Std 1003.1 in order to handle VxWorks tasks' attributes. \me INCLUDE FILES: pthread.h SEE ALSO: taskLib, semMLib, semPxLib \INTERNAL PTHREAD TERMINATION POSIX specifies only two ways for a pthread to terminate: go through the pthread_exit() API either explicitly or implicitly (i.e. when the thread's root function simply returns), or get canceled (i.e. another pthread called the pthread_cancel() API against the to-be-canceled pthread). On VxWorks however a third way of terminating a pthread is available although -on purpose- not documented: deleting the VxWorks task underlying the POSIX thread. This library implements the necessary logic to cleanly terminate a pthread via any of those three ways: all the resources of the exiting pthread are freed or released, with care to respect the POSIX specs when applicable (most notably that a joinable pthread going away will leave behind it a memory storage holding its exit status; that memory storage is released by any other pthread calling the pthread_join() API). At this time only pthread_join() prevents asynchronous cancellations while holding another pthread's lock. All other pthread API can be cancelled harmlessly while executing the critical section (the lock is released in pthread_exit()). If the implementation of any other pthread API changes so that there is a risk of leaving a pthread's control block in an incoherent state then that API must implement the same type of logic as pthread_join() does regarding asynchronous cancellations. */ /* includes */ #include #include #include #include #include #include #include #include #include #include /* pthreadLibInit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __PTHREAD_SRC #include #undef PTHREADS_DEBUG #ifdef PTHREADS_DEBUG #undef LOCAL #define LOCAL #endif /* PTHREADS_DEBUG */ /* defines */ /* * Implements an alternate interpretation of the dynamic between * pthread_detach() and pthread_join() by which joinable pthreads that * dynamically detached are still considered joinable but would get their * resources freed by pthread_exit() in case nobody joined them before they * exited. * * This alternate interpretation is fine by the POSIX PSE52 conformance's * point of view but introduces some backward compatibility concerns. */ #define DETACHED_PTHREADS_MAY_BE_JOINED FALSE /* * SELF_PTHREAD_ID is a shortcut for access to the address of the currently * executing thread's control block (acting as thread ID). */ #define SELF_PTHREAD_ID ((pthreadCB *)(((WIND_TCB *)(taskIdSelf()))->pPthread)) /* * PTHREAD_TO_NATIVE_TASK provides the ID of the VxWorks task underlying the * POSIX thread. */ #define PTHREAD_TO_NATIVE_TASK(thId) (((pthreadCB *)(thId))->vxTaskId) /* * VALID_PTHREAD verifies whether the pthread data is valid and whether * the pthread ID corresponds to that registered for the underlying VxWorks * task (which means the pthread ID is not stale). */ #define VALID_PTHREAD(pThread, pTcb) \ ((pThread) && (((pThread)->flags & VALID) == VALID) && \ (((pThread)->flags & TASK_EXITED) == 0) && \ ((pTcb) = (WIND_TCB *)(pThread)->vxTaskId) && \ (pTcb)->pPthread && \ (pTcb)->pPthread == (pThread)) /* * POSIX thread's mutex can be statically initialized, i.e. they do not * always require a call to pthread_mutex_init() before they are used. * Since POSIX thread mutexes are implemented on top of VxWorks' mutual * exclusion semaphores, which by design require a call to the semMCreate() * routine, we can not actually provide statically initialized mutex. * The solution is to have any pthread routine which manipulates a mutex * initialize the underlying VxWorks semaphore if it is not already * initialized. Since mutex are, by nature, objects shared between threads * we need to ensure mutual exclusion around the initialization sequence as * well. This is what the INIT_MUTEX macro below is all about. The first * check is not protected because in the immense majority of the cases the * mutex is already initialized, then there is nothing to do, so we don't * want to get the performance penalty of mutual exclusion. If however the * mutex appears to be still uninitialized then we go and try to initialize * it. This second part is under mutual exclusion protection and re-checks * whether the mutex is now initialized. This prevents race conditions in * case more than one thread managed to see the mutex as uninitialized: * only one of them will perform the initialization, the others will then * skip this part. */ #define MUTEX_INIT_COMPLETE(pMutex) \ if (pMutex->mutexInitted != PTHREAD_INITIALIZED_OBJ) \ { \ /* \ * The mutex appears uninitialized still. First, protect the \ * mutex initialization sequence from concurrent calls. \ */ \ \ if (semTake (pthreadLibMutexInitSemId, WAIT_FOREVER) == ERROR) \ return EINVAL; /* internal error */ \ \ if (pMutex->mutexInitted != PTHREAD_INITIALIZED_OBJ) \ { \ /* \ * If we come here, no one else has initialized this mutex \ * yet, so we do it. \ */ \ \ pMutex->mutexSemId = semMCreate ( \ ((pMutex->mutexAttr.mutexAttrProtocol == \ PTHREAD_PRIO_INHERIT) ? SEM_INVERSION_SAFE : 0) | \ SEM_DELETE_SAFE | SEM_Q_PRIORITY); \ if (pMutex->mutexSemId == SEM_ID_NULL) \ { \ (void)semGive (pthreadLibMutexInitSemId); \ return EINVAL; /* internal error */ \ } \ \ *((volatile int *)&(pMutex)->mutexInitted) = \ PTHREAD_INITIALIZED_OBJ; \ } \ \ if (semGive (pthreadLibMutexInitSemId) == ERROR) \ return EINVAL; /* internal error */ \ } /* * The very same logic as for the POSIX thread mutexes applies to the * POSIX thread condition variables. Read it above. Note also that * pCond may contain initialized info passed by user code, thus * INIT_COND needs to be careful not to modify those fields. */ #define COND_INIT_COMPLETE(pCond) \ if ((pCond)->condInitted != PTHREAD_INITIALIZED_OBJ) \ { \ /* \ * Protect condition variable initialization sequence from \ * concurrent calls. \ */ \ \ if (semTake (pthreadLibCondInitSemId, WAIT_FOREVER) == ERROR) \ return EINVAL; /* internal error */ \ \ if ((pCond)->condInitted != PTHREAD_INITIALIZED_OBJ) \ { \ /* \ * If we come here, no one else has initialized this \ * condition variable yet, so we do it. \ */ \ \ if (((pCond)->condSemId = semBCreate((SEM_Q_PRIORITY | \ SEM_KERNEL_INTERRUPTIBLE), \ SEM_EMPTY)) == SEM_ID_NULL) \ { \ (void)semGive (pthreadLibCondInitSemId); \ return (EINVAL); /* internal error */ \ } \ \ *((volatile int *)&(pCond)->condInitted) = \ PTHREAD_INITIALIZED_OBJ; \ } \ if (semGive (pthreadLibCondInitSemId) == ERROR) \ return EINVAL; \ } /* numeric boundary check for priority. no mention of meaning of value */ #define PRIO_LOWER_BOUND 0 #define PRIO_UPPER_BOUND 255 #define VALID_PRIORITY(pri) (PRIO_LOWER_BOUND <= (pri) && \ (pri) <= PRIO_UPPER_BOUND) #define PX_VX_PRI_CONV(mode,pri) (mode ? (POSIX_HIGH_PRI - pri) : pri) #define DEF_PRIORITY (PRIO_LOWER_BOUND + \ (PRIO_UPPER_BOUND - PRIO_LOWER_BOUND) / 2) /* Simple pthread lock/unlock macro pair */ #define PTHREAD_LOCK(pThread) \ { \ if (semTake ((pThread)->lock, WAIT_FOREVER) == ERROR) \ return EINVAL; \ } #define PTHREAD_UNLOCK(pThread) (void)semGive ((pThread)->lock) /* Pthread validate-and-lock/unlock-validated macro pair */ #define PTHREAD_VALIDATE_AND_LOCK(pThread, self) \ pthreadValidateAndLock (pThread, self) #define PTHREAD_VALIDATED_UNLOCK(pThread, self) \ { \ PTHREAD_UNLOCK (pThread); \ self->lockedPthread = 0; \ } /* * Key status definitions. * * SPR# 114293: It is necessary to determine that a key has been * created but has not been used (which is done by a * call to pthread_key_setspecific). These definitions * permit this detection. * * KEY_FREE : the key slot in the key_table is FREE * KEY_CREATED : The key slot in the key_table is in use but * does not contain valid data. * (a destructor is bound to the key) * KEY_IN_USE : The key slot in the key_table is in use and * contains valid data */ #define KEY_FREE 0 #define KEY_CREATED 1 #define KEY_IN_USE 2 /* typedefs */ typedef struct { pthread_mutex_t mutex; long keyStatus; void (*destructor)(); } pthread_key; /* globals */ /* * This is a common. DO NOT INITIALISE IT - doing so will break the scalability * that relies on its status as a common. */ FUNCPTR _pthread_setcanceltype; /* locals */ LOCAL pthread_key key_table[_POSIX_THREAD_KEYS_MAX]; LOCAL SEM_ID key_mutex = NULL; LOCAL SEM_ID pthreadLibOnceSemId = NULL; LOCAL SEM_ID pthreadLibMutexInitSemId = NULL; LOCAL SEM_ID pthreadLibCondInitSemId = NULL; LOCAL SEM_ID pthreadLibSemId = NULL; /* The default mutex, condition variable and thread creation attribute objects * and initializers - either POSIX or implementation-defined. * Used by: * pthread_mutex_init() * pthread_cond_init() * pthread_create() * * For mutexes and condition variables, * only the 'process-shared' attribute is defined. */ LOCAL pthread_mutexattr_t defaultMutexAttr = { PTHREAD_INITIALIZED_OBJ, PTHREAD_PRIO_INHERIT, 0 }; /* forward declarations */ LOCAL void self_become_pthread(void); LOCAL void pthread_abnormal_exit (pthreadCB * pThread); LOCAL void pthreadCtrlBlockFree (pthreadCB * pThread); LOCAL void cleanupPrivateData (pthreadCB * pThread); LOCAL STATUS pthreadValidateAndLock (pthreadCB * pThread, pthreadCB * selfPthreadId); _WRS_INLINE pthreadCB * _pthread_id_self (void); _WRS_INLINE void _pthread_testcancel_inline (pthreadCB * pThread); _WRS_INLINE int _pthread_setcanceltype_inline (int type, int * oldtype, pthreadCB *pThread); static const void ** pthread_key_allocate_data (void); LOCAL void deadlock (void); LOCAL void sigCancelHandler (int dummy); LOCAL void wrapperFunc (void * (*function)(void *), void * arg, const sigset_t creatorSigMask); _WRS_INLINE void _pthread_onexit_cleanup (pthreadCB * pThread, BOOL abnormalTermination); /******************************************************************************* * * pthreadLibInit - initialize POSIX threads support * * This routine initializes the POSIX threads () support for * VxWorks. It should be called before any POSIX threads functions are used; * normally it will be called as part of the kernel's initialization sequence. * * RETURNS: N/A * * \NOMANUAL */ void pthreadLibInit (void) { if (_pthreadLibInit (pthread_abnormal_exit) != ERROR) { /* * Mutual exclusion semaphore protecting the validation * of the pThread */ if ((pthreadLibSemId = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE)) == SEM_ID_NULL) { EDR_INIT_FATAL_INJECT (NULL, NULL, NULL, NULL, "pthreadLib: cannot create pThreadLibSemId " "mutex."); } /* Create the thread specific data key */ if ((key_mutex = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE)) == SEM_ID_NULL) { EDR_INIT_FATAL_INJECT (NULL, NULL, NULL, NULL, "pthreadLib: cannot create key_mutex."); } /* * Create the mutual exclusion semaphore protecting the use of the * pthread_once() routine. */ if ((pthreadLibOnceSemId = semMCreate (SEM_Q_PRIORITY)) == SEM_ID_NULL) { EDR_INIT_FATAL_INJECT (NULL, NULL, NULL, NULL, "pthreadLib: cannot create " "pthreadLibOnceSemIdmutex."); } /* * Create the mutual exclusion semaphore protecting the initialization * of the pthread mutex. */ if ((pthreadLibMutexInitSemId = semMCreate (SEM_Q_PRIORITY)) == SEM_ID_NULL) { EDR_INIT_FATAL_INJECT (NULL, NULL, NULL, NULL, "pthreadLib: cannot create " "pthreadLibMutexInitSemId."); } /* * Create the mutual exclusion semaphore protecting the initialization * of the pthread conditional variable. */ if ((pthreadLibCondInitSemId = semMCreate (SEM_Q_PRIORITY)) == SEM_ID_NULL) { EDR_INIT_FATAL_INJECT (NULL, NULL, NULL, NULL, "pthreadLib: cannot create " "pthreadLibCondInitSemId."); } /* * Initialize the function pointer for user level access to this * feature. */ _pthread_setcanceltype = (FUNCPTR) pthread_setcanceltype; } } /* * Section 3 - Process Primitives */ /******************************************************************************* * * pthread_sigmask - change and/or examine calling thread's signal mask (POSIX) * * This routine changes the signal mask for the calling thread as described * by the and arguments. If is not NULL, the previous * signal mask is stored in the location pointed to by it. * * The value of indicates the manner in which the set is changed and * consists of one of the following defined in 'signal.h': * \is * \i SIG_BLOCK * The resulting set is the union of the current set and the signal set * pointed to by . * \i SIG_UNBLOCK * The resulting set is the intersection of the current set and the complement * of the signal set pointed to by . * \i SIG_SETMASK * The resulting set is the signal set pointed to by . * \ie * * RETURNS: On success zero; on failure a 'EINVAL' error code is returned. * * ERRNO: N/A * * SEE ALSO: kill(), pthread_kill(), sigprocmask(), sigaction(), * sigsuspend(), sigwait() */ int pthread_sigmask ( int how, /* method for changing set */ const sigset_t * set, /* new set of signals */ sigset_t * oset /* old set of signals */ ) { if (sigprocmask(how, (sigset_t *) set, oset) == ERROR) return (EINVAL); return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_kill - send a signal to a thread (POSIX) * * This routine sends signal number to the thread specified by . * The signal is delivered and handled as described for the kill() function. * * RETURNS: On success zero; on failure one of the following non-zero error * codes: 'ESRCH', 'EINVAL' * * ERRNO: N/A * * SEE ALSO: kill(), pthread_sigmask(), sigprocmask(), sigaction(), * sigsuspend(), sigwait() * * INTERNAL * The taskKill() system call (indirectly) sets the errno to EAGAIN when on * failure. Since this value can be directly returned by pthread_kill() we * have a conflict with IEEE Std 1003.1 here... */ int pthread_kill ( pthread_t thread, /* thread to signal */ int sig /* signal to send */ ) { pthreadCB * pThread = (pthreadCB *)thread; WIND_TCB * pTcb; if ((sig < 0) || (sig > _NSIGS)) return (EINVAL); /* * Let's simply validate the targeted pthread. Locking it would be * overkill since we do not need to modify its control block. * There is a possibility that the targeted pthread is canceled or * deleted during or after the validation. However the kill() API * verifies the task ID for us. */ if (VALID_PTHREAD (pThread, pTcb) == FALSE) return (ESRCH); if ((sig == 0) || (kill ((TASK_ID)pTcb, sig)) == OK) return (_RETURN_PTHREAD_SUCCESS); else return (errno); } /* * Section 11.3 - Mutexes */ /******************************************************************************* * * pthread_mutexattr_init - initialize mutex attributes object (POSIX) * * This routine initializes the mutex attribute object and fills it * with default values for the attributes: * .iP "'Mutex Protocol'" 4 * PTHREAD_PRIO_INHERIT - the priority of the owner thread is temporarily raised * if a higher priority thread is blocked on the mutex. * .iP "'Mutex Priority Ceiling'" * 0 - lowest priority. * .LP * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_destroy(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_getprotocol(), pthread_mutexattr_setprioceiling(), * pthread_mutexattr_setprotocol(), pthread_mutex_init() */ int pthread_mutexattr_init ( pthread_mutexattr_t * pAttr /* mutex attributes */ ) { if (pAttr == NULL) return (EINVAL); /* SPR# 114360 - added initialization to other members of struct */ pAttr->mutexAttrStatus = PTHREAD_INITIALIZED_OBJ; pAttr->mutexAttrProtocol = PTHREAD_PRIO_INHERIT; pAttr->mutexAttrPrioceiling = 0; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutexattr_destroy - destroy mutex attributes object (POSIX) * * This routine destroys a mutex attribute object. The mutex attribute object * must not be reused until it is reinitialized. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_getprioceiling(), pthread_mutexattr_getprotocol(), * pthread_mutexattr_init(), pthread_mutexattr_setprioceiling(), * pthread_mutexattr_setprotocol(), pthread_mutex_init() */ int pthread_mutexattr_destroy ( pthread_mutexattr_t * pAttr /* mutex attributes */ ) { if (pAttr == NULL) return (EINVAL); pAttr->mutexAttrStatus = PTHREAD_DESTROYED_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutexattr_setprotocol - set protocol attribute in mutex attribute object (POSIX) * * This function selects the locking protocol to be used when a mutex is created * using this attributes object. The protocol to be selected is either * PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT or PTHREAD_PRIO_PROTECT. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i ENOTSUP * \ie * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_destroy(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_getprotocol(), pthread_mutexattr_init(), * pthread_mutexattr_setprioceiling(), pthread_mutex_init() */ int pthread_mutexattr_setprotocol ( pthread_mutexattr_t * pAttr, /* mutex attributes */ int protocol /* new protocol */ ) { if ((pAttr == NULL) || pAttr->mutexAttrStatus != PTHREAD_INITIALIZED_OBJ) return (EINVAL); if (protocol != PTHREAD_PRIO_NONE && protocol != PTHREAD_PRIO_INHERIT && protocol != PTHREAD_PRIO_PROTECT) { return (ENOTSUP); } pAttr->mutexAttrProtocol = protocol; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutexattr_getprotocol - get value of protocol in mutex attributes object (POSIX) * * This function gets the current value of the protocol attribute in a mutex * attributes object. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_destroy(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_init(), pthread_mutexattr_setprioceiling(), * pthread_mutexattr_setprotocol(), pthread_mutex_init() */ int pthread_mutexattr_getprotocol ( pthread_mutexattr_t * pAttr, /* mutex attributes */ int * pProtocol /* current protocol (out) */ ) { if ((pAttr == NULL) || (pAttr->mutexAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); *pProtocol = pAttr->mutexAttrProtocol; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutexattr_setprioceiling - set prioceiling attribute in mutex attributes object (POSIX) * * This function sets the value of the prioceiling attribute in a mutex * attributes object. Unless the protocol attribute is set to * PTHREAD_PRIO_PROTECT, this attribute is ignored. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_destroy(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_getprotocol(), pthread_mutexattr_init(), * pthread_mutexattr_setprotocol(), pthread_mutex_init() */ int pthread_mutexattr_setprioceiling ( pthread_mutexattr_t * pAttr, /* mutex attributes */ int prioceiling /* new priority ceiling */ ) { if ((pAttr == NULL) || (pAttr->mutexAttrStatus != PTHREAD_INITIALIZED_OBJ) || (prioceiling < PRIO_LOWER_BOUND || prioceiling > PRIO_UPPER_BOUND)) { return (EINVAL); } pAttr->mutexAttrPrioceiling = prioceiling; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutexattr_getprioceiling - get the current value of the prioceiling attribute in a mutex attributes object (POSIX) * * This function gets the current value of the prioceiling attribute in a mutex * attributes object. Unless the value of the protocol attribute is * PTHREAD_PRIO_PROTECT, this value is ignored. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutexattr_destroy(), pthread_mutexattr_getprotocol(), * pthread_mutexattr_init(), pthread_mutexattr_setprioceiling(), * pthread_mutexattr_setprotocol(), pthread_mutex_init() */ int pthread_mutexattr_getprioceiling ( pthread_mutexattr_t * pAttr, /* mutex attributes */ int * pPrioceiling /* current priority ceiling (out) */ ) { if ((pAttr == NULL) || (pAttr->mutexAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); *pPrioceiling = pAttr->mutexAttrPrioceiling; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_getprioceiling - get the value of the prioceiling attribute of a mutex (POSIX) * * This function gets the current value of the prioceiling attribute of a mutex. * Unless the mutex was created with a protocol attribute value of * PTHREAD_PRIO_PROTECT, this value is meaningless. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_mutex_setprioceiling(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_setprioceiling() */ int pthread_mutex_getprioceiling ( pthread_mutex_t * pMutex, /* pthread mutex */ int * pPrioceiling /* current priority ceiling (out) */ ) { if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); MUTEX_INIT_COMPLETE(pMutex); *pPrioceiling = pMutex->mutexAttr.mutexAttrPrioceiling; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_setprioceiling - dynamically set the prioceiling attribute of a mutex (POSIX) * * This function dynamically sets the value of the prioceiling attribute of a * mutex. Unless the mutex was created with a protocol value of * PTHREAD_PRIO_PROTECT, this function does nothing. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i EPERM * \i 'S_objLib_OBJ_ID_ERROR' * \i 'S_semLib_NOT_ISR_CALLABLE' * \ie * * ERRNO: N/A * * SEE ALSO: pthread_mutex_getprioceiling(), pthread_mutexattr_getprioceiling(), * pthread_mutexattr_setprioceiling() */ int pthread_mutex_setprioceiling ( pthread_mutex_t * pMutex, /* pthread mutex */ int prioceiling, /* new priority ceiling */ int * pOldPrioceiling /* old priority ceiling (out) */ ) { if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); /* SPR# 117769 - checks to see if prioceiling is out of range */ if ((prioceiling < PRIO_LOWER_BOUND) || (prioceiling > PRIO_UPPER_BOUND)) return (EINVAL); MUTEX_INIT_COMPLETE(pMutex); if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) return (EINVAL); /* internal error */ /* SPR# 117249 - checks to see if pOldPrioceiling is NULL */ if (pOldPrioceiling) *pOldPrioceiling = pMutex->mutexAttr.mutexAttrPrioceiling; pMutex->mutexAttr.mutexAttrPrioceiling = prioceiling; /* Clear errno before trying to test it */ if (semGive(pMutex->mutexSemId) == ERROR) { if (errno == S_semLib_INVALID_OPERATION) return (EPERM); /* not owner - can't happen */ else return (errno); /* some other error */ } return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_init - initialize mutex from attributes object (POSIX) * * This routine initializes the mutex object pointed to by according * to the mutex attributes specified in . If is NULL, default * attributes are used as defined in the POSIX specification. If is * non-NULL then it is assumed to point to a mutex attributes object initialized * by pthread_mutexattr_init(), and those are the attributes used to create the * mutex. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \ie * * ERRNO: N/A * * SEE ALSO: semLib, semMLib, pthread_mutex_destroy(), pthread_mutex_lock(), * pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_init(), * semMCreate() */ int pthread_mutex_init ( pthread_mutex_t * pMutex, /* pthread mutex */ const pthread_mutexattr_t * pAttr /* mutex attributes */ ) { pthread_mutexattr_t *pSource; if (pMutex == NULL) return (EINVAL); pSource = pAttr ? (pthread_mutexattr_t *)pAttr : &defaultMutexAttr; bcopy((const char *)pSource, (char *)&pMutex->mutexAttr, sizeof (pthread_mutexattr_t)); pMutex->mutexSemId = NULL; pMutex->mutexValid = PTHREAD_VALID_OBJ; pMutex->mutexInitted = PTHREAD_UNUSED_YET_OBJ; pMutex->mutexCondRefCount = 0; pMutex->mutexSavPriority = -1; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_destroy - destroy a mutex (POSIX) * * This routine destroys a mutex object, freeing the resources it might hold. * The mutex can be safely destroyed when unlocked. On VxWorks a thread may * destroy a mutex that it owns (i.e. that the thread has locked). If the * mutex is locked by an other thread this routine will return an error * ('EBUSY'). * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i EBUSY * \ie * * ERRNO: N/A * * SEE ALSO: semLib, semMLib, pthread_mutex_init(), pthread_mutex_lock(), * pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_init(), * semDelete() */ int pthread_mutex_destroy ( pthread_mutex_t * pMutex /* pthread mutex */ ) { TASK_ID owner; if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); /* * POSIX says that it is allowed to destroy an initialized but * unlocked (i.e. no owner) mutex. Let's move the mutex initialization of * the mutex earlier (SPR #101108). */ MUTEX_INIT_COMPLETE(pMutex); if (pMutex->mutexCondRefCount) return (EBUSY); owner = _pthreadSemOwnerGet (pMutex->mutexSemId); if ((owner != 0) && (owner != taskIdSelf())) return (EBUSY); /* * Underlying VxWorks semaphore is acquired to make sure * that it can be deleted */ if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) return (EINVAL); (void)pthread_mutexattr_destroy(&(pMutex->mutexAttr)); if (semDelete(pMutex->mutexSemId) == ERROR) return (EINVAL); pMutex->mutexValid = PTHREAD_INVALID_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_lock - lock a mutex (POSIX) * * This routine locks the mutex specified by . If the mutex is * currently unlocked, it becomes locked, and is said to be owned by the * calling thread. In this case pthread_mutex_lock() returns immediately. * * If the mutex is already locked by another thread, pthread_mutex_lock() * blocks the calling thread until the mutex is unlocked by its current owner. * * If it is already locked by the calling thread, pthread_mutex_lock will * deadlock on itself and the thread will block indefinitely. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: semLib, semMLib, pthread_mutex_init(), pthread_mutex_lock(), * pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_init(), * semTake() */ int pthread_mutex_lock ( pthread_mutex_t * pMutex /* POSIX mutex */ ) { pthreadCB * tid; /* caller thread */ int priority; /* underlying VxWorks task's priority */ if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); MUTEX_INIT_COMPLETE(pMutex); /* Get the ID of the caller; make the caller a POSIX thread if needed */ tid = _pthread_id_self(); /* * The following statement checks the assertion that, when the mutex * protocol is set to PTHREAD_PRIO_PROTECT, the routine should fail with * EINVAL if the calling thread's priority is higher (i.e. is lower in * terms of VxWorks priority range) than the mutex's priority ceiling. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) { (void) taskPriorityGet (PTHREAD_TO_NATIVE_TASK (tid), &priority); if (priority < PX_VX_PRIORITY_CONVERT(pMutex->mutexAttr.mutexAttrPrioceiling)) return (EINVAL); /* would violate priority ceiling */ } /* * Plain old POSIX mutexes deadlock if the owner of a lock tries to * acquire it again. The Open Group's Unix98 standard as well as POSIX * P1003.1j Draft standard allow different lock types that at this point * would have different behavior - an error checking mutex would return * EDEADLK, and a recursive lock would bump a ref count and return * success. * * XXX PAD - we should port to the kernel the new mutex types supported * in RTP. */ if (_pthreadSemOwnerGet (pMutex->mutexSemId) == PTHREAD_TO_NATIVE_TASK(tid)) deadlock(); /* * If the mutex protocol is set to PTHREAD_PRIO_PROTECT the priority of * the calling thread is raised to the ceiling. We know (see logic above) * that the calling thread's priority is not higher than the ceiling so we * can safely directly set the new priority (a no-op if the thread's * priority is equal to the ceiling). * * Note that this has to happened before the VxWorks semaphore is taken to * avoid priority inversion but there is a window during which the thread's * priority is elevated although it has not yet taken the underlying * VxWorks semaphore. This cannot be solved satisfactorily without * getting native support for priority ceiling in vxWorks semaphores. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) { (void)taskPrioritySet (PTHREAD_TO_NATIVE_TASK (tid), PX_VX_PRIORITY_CONVERT(pMutex->mutexAttr.mutexAttrPrioceiling)); } /* * Some other internal error occurred, lets go back to the original * priority if the PTHREAD_PRIO_PROTECT protocol is used. */ if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) { if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) (void)taskPrioritySet (PTHREAD_TO_NATIVE_TASK (tid), priority); return (EINVAL); } /* * If this is a priority ceiling mutex, store the thread's original * priority in the mutex structure now that the mutex is safely acquired * by the thread. Doing this allows for acquiring nested priority ceiling * mutex while restoring the appropriate priority level when each of * those mutex are released in turn. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) pMutex->mutexSavPriority = priority; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_trylock - lock mutex if it is available (POSIX) * * This routine locks the mutex specified by . If the mutex is * currently unlocked, it becomes locked and owned by the calling thread. In * this case pthread_mutex_trylock() returns immediately. * * If the mutex is already locked by another thread, pthread_mutex_trylock() * returns immediately with the error code 'EBUSY'. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i EBUSY * \ie * * ERRNO: N/A * * SEE ALSO: semLib, semMLib, pthread_mutex_init(), pthread_mutex_lock(), * pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_init(), * semTake() */ int pthread_mutex_trylock ( pthread_mutex_t * pMutex /* pthread mutex */ ) { pthreadCB * tid; /* caller thread */ int priority; /* underlying VxWorks task's priority */ if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); MUTEX_INIT_COMPLETE(pMutex); /* Get the ID of the caller; make the caller a POSIX thread if needed */ tid = _pthread_id_self(); /* * The following statement checks the assertion that, when the mutex * protocol is set to PTHREAD_PRIO_PROTECT, the routine should fail with * EINVAL if the calling thread's priority is higher (i.e. is lower in * terms of VxWorks priority range) than the mutex's priority ceiling. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) { (void) taskPriorityGet (PTHREAD_TO_NATIVE_TASK (tid), &priority); if (priority < PX_VX_PRIORITY_CONVERT(pMutex->mutexAttr.mutexAttrPrioceiling)) return (EINVAL); /* would violate priority ceiling */ } if (_pthreadSemOwnerGet (pMutex->mutexSemId) == PTHREAD_TO_NATIVE_TASK(tid)) return(EBUSY); /* * If the mutex protocol is set to PTHREAD_PRIO_PROTECT the priority of * the calling thread is raised to the ceiling. We know (see logic above) * that the calling thread's priority is not higher than the ceiling so we * can safely directly set the new priority (a no-op if the thread's * priority is equal to the ceiling). * * Note that this has to happened before the VxWorks semaphore is taken to * avoid priority inversion but there is a window during which the thread's * priority is elevated although it has not yet taken the underlying * VxWorks semaphore. This cannot be solved satisfactorily without * getting native support for priority ceiling in vxWorks semaphores. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) { (void)taskPrioritySet (PTHREAD_TO_NATIVE_TASK (tid), PX_VX_PRIORITY_CONVERT(pMutex->mutexAttr.mutexAttrPrioceiling)); } /* * The mutex could not be locked, lets go back to the original * priority if the PTHREAD_PRIO_PROTECT protocol is used. */ if (semTake(pMutex->mutexSemId, NO_WAIT) == ERROR) { if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) (void)taskPrioritySet (PTHREAD_TO_NATIVE_TASK (tid), priority); return (EBUSY); } /* * If this is a priority ceiling mutex, store the thread's original * priority in the mutex structure now that the mutex is safely acquired * by the thread. Doing this allows for acquiring nested priority ceiling * mutex while restoring the appropriate priority level when each of * those mutex are released in turn. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) pMutex->mutexSavPriority = priority; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_mutex_unlock - unlock a mutex (POSIX) * * This routine unlocks the mutex specified by . If the calling thread * is not the current owner of the mutex, pthread_mutex_unlock() returns with * the error code 'EPERM'. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i EPERM * \i 'S_objLib_OBJ_ID_ERROR' * \i 'S_semLib_NOT_ISR_CALLABLE' * \ie * * ERRNO: N/A * * SEE ALSO: semLib, semMLib, pthread_mutex_init(), pthread_mutex_lock(), * pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_init(), * semGive() */ int pthread_mutex_unlock ( pthread_mutex_t * pMutex /* pthread mutex */ ) { pthreadCB * tid; /* caller thread */ int priority = 255; /* Underlying VxWorks task's priority, * initialized to 255 for conformance. When * setting its priority it will use * pMutex->mutexSavPriority. */ int status = _RETURN_PTHREAD_SUCCESS; if ((pMutex == NULL) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); MUTEX_INIT_COMPLETE(pMutex); /* Get the ID of the caller; make the caller a POSIX thread if needed */ tid = _pthread_id_self(); if (_pthreadSemOwnerGet (pMutex->mutexSemId) != PTHREAD_TO_NATIVE_TASK(tid)) return (EPERM); /* * For priority ceiling mutex the thread's original priority kept in the * mutex structure must be reset while the mutex is still acquired but * the thread priority cannot be returned to its original value until * after the mutex has been released to prevent unduly delaying higher * priority threads that are blocked on this mutex, so we make a copy of * the original priority and reset the mutex structure's field now. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) { priority = pMutex->mutexSavPriority; pMutex->mutexSavPriority = -1; } if (semGive(pMutex->mutexSemId) == ERROR) { if (errno == S_semLib_INVALID_OPERATION) status = EPERM; /* not owner */ else status = errno; /* some other error */ } /* * In case the mutex protocol is PTHREAD_PRIO_PROTECT lets reset the * thread's priority to what it was originally. * * Note that there is a window during which the thread's priority is still * elevated although it does not hold the underlying VxWorks semaphore * anymore. This cannot be solved satisfactorily without getting native * support for priority ceiling in vxWorks semaphores. */ if (pMutex->mutexAttr.mutexAttrProtocol == PTHREAD_PRIO_PROTECT) (void)taskPrioritySet (PTHREAD_TO_NATIVE_TASK (tid), priority); return status; } /* * Section 11.4 - Condition Variables */ /******************************************************************************* * * pthread_condattr_init - initialize a condition attribute object (POSIX) * * This routine initializes the condition attribute object and fills * it with default values for the attributes. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \ie * * ERRNO * * SEE ALSO: pthread_cond_init(), pthread_condattr_destroy() */ int pthread_condattr_init ( pthread_condattr_t * pAttr /* condition variable attributes */ ) { if (pAttr == NULL) return (EINVAL); pAttr->condAttrStatus = PTHREAD_INITIALIZED_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_condattr_destroy - destroy a condition attributes object (POSIX) * * This routine destroys the condition attribute object . It must * not be reused until it is reinitialized. * * RETURNS: Always returns zero. * * ERRNO: None. * * SEE ALSO: pthread_cond_init(), pthread_condattr_init() */ int pthread_condattr_destroy ( pthread_condattr_t * pAttr /* condition variable attributes */ ) { pAttr->condAttrStatus = PTHREAD_DESTROYED_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_init - initialize condition variable (POSIX) * * DESCRIPTION * * This function initializes a condition variable. A condition variable * is a synchronization device that allows threads to block until some * predicate on shared data is satisfied. The basic operations on conditions * are to signal the condition (when the predicate becomes true), and wait * for the condition, blocking the thread until another thread signals the * condition. * * A condition variable must always be associated with a mutex to avoid a * race condition between the wait and signal operations. * * If is NULL then the default attributes are used as specified by * POSIX; if is non-NULL then it is assumed to point to a condition * attributes object initialized by pthread_condattr_init(), and those are * the attributes used to create the condition variable. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \ie * * ERRNO * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_broadcast(), pthread_cond_destroy(), pthread_cond_signal(), * pthread_cond_timedwait(), pthread_cond_wait() */ int pthread_cond_init ( pthread_cond_t * pCond, /* condition variable */ pthread_condattr_t * pAttr /* condition variable attributes */ ) { if (pCond == NULL) return (EINVAL); /* * Although we do not currently need to use the condition variable * attributes to initialize the condition variable object, the conformance * requires that the attributes initialization is verified if a non-null * pAttr argument is passed to this routine (SPR #117221). */ if (pAttr && (pAttr->condAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); /* in VxWorks, there is nothing useful in a pthread_condattr_t */ pCond->condValid = PTHREAD_VALID_OBJ; pCond->condSemId = NULL; pCond->condInitted = PTHREAD_UNUSED_YET_OBJ; pCond->condMutex = NULL; pCond->condRefCount = 0; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_destroy - destroy a condition variable (POSIX) * * This routine destroys the condition variable pointed to by . No * threads can be waiting on the condition variable when this function is * called. If there are threads waiting on the condition variable, then * pthread_cond_destroy() returns 'EBUSY'. * * RETURNS: On success zero; on failure a non-zero error code. * \is * \i EINVAL * \i EBUSY * \ie * * ERRNO * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_broadcast(), pthread_cond_init(), pthread_cond_signal(), * pthread_cond_timedwait(), pthread_cond_wait() */ int pthread_cond_destroy ( pthread_cond_t * pCond /* condition variable */ ) { if ((pCond == NULL) || (pCond->condValid != PTHREAD_VALID_OBJ)) return (EINVAL); if (pCond->condRefCount != 0) return (EBUSY); /* someone else blocked on *pCond */ COND_INIT_COMPLETE(pCond); if (semDelete(pCond->condSemId) == ERROR) return (errno); pCond->condValid = PTHREAD_INVALID_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_signal - unblock a thread waiting on a condition (POSIX) * * This routine unblocks one thread waiting on the specified condition * variable . If no threads are waiting on the condition variable then * this routine does nothing; if more than one thread is waiting, then one will * be released, but it is not specified which one. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_broadcast(), pthread_cond_destroy(), pthread_cond_init(), * pthread_cond_timedwait(), pthread_cond_wait() */ int pthread_cond_signal ( pthread_cond_t * pCond /* condition variable */ ) { TASK_ID taskIdList[1]; if ((pCond == NULL) || (pCond->condValid != PTHREAD_VALID_OBJ)) return (EINVAL); COND_INIT_COMPLETE(pCond); if (semInfo(pCond->condSemId, taskIdList, 1) == 0) { /* * There are no threads waiting on the signal, so * do nothing and return all-is-well. */ return (_RETURN_PTHREAD_SUCCESS); } /* Otherwise unblock one of the pended threads */ if (semGive (pCond->condSemId) == ERROR) return (EINVAL); /* internal error */ return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_broadcast - unblock all threads waiting on a condition (POSIX) * * This function unblocks all threads blocked on the condition variable * . Nothing happens if no threads are waiting on the specified condition * variable. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_destroy(), pthread_cond_init(), pthread_cond_signal(), * pthread_cond_timedwait(), pthread_cond_wait() */ int pthread_cond_broadcast ( pthread_cond_t * pCond /* condition variable */ ) { if ((pCond == NULL) || (pCond->condValid != PTHREAD_VALID_OBJ)) return (EINVAL); COND_INIT_COMPLETE(pCond); /* * Let's unblock all threads pending on the condition variable. The * scheduler will elect the one thread that will get to run and the others * will just stay in ready mode and wait for their turn. */ if (semFlush (pCond->condSemId) == ERROR) return (EINVAL); /* internal error */ return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_wait - wait for a condition variable (POSIX) * * This function atomically releases the mutex and waits for the * condition variable to be signalled by another thread. The mutex * must be locked by the calling thread when pthread_cond_wait() is called; if * it is not then this function returns an error ('EINVAL'). * * Before returning to the calling thread, pthread_cond_wait() re-acquires the * mutex. * * If the calling thread receives a signal while pending on the condition * variable pthread_cond_wait() will return zero, indicating a spurious wakeup. * Because of this, it is important for the caller to re-evaluate the predicate * upon return. * * If the calling thread gets cancelled while pending on the condition * variable pthread_cond_wait() will also re-acquire the mutex prior to * executing the cancellation cleanup handlers (if any). The mutex will * however be released prior to the thread exiting so that this mutex can be * used by other threads. * * RETURNS: zero on success or spurious wakeup; on failure the 'EINVAL' error * code. * * ERRNO: N/A * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_broadcast(), pthread_cond_destroy(), pthread_cond_init(), * pthread_cond_signal(), pthread_cond_timedwait() */ int pthread_cond_wait ( pthread_cond_t * pCond, /* condition variable */ pthread_mutex_t * pMutex /* pthread mutex */ ) { TASK_ID mutexOwner; int savtype; int callerSavedPrio; /* original priority of caller saved by pthread_mutex_lock() */ pthreadCB * selfPthreadId = SELF_PTHREAD_ID; /* validate the parameters SPR# 114274 */ if ((pCond == NULL) || (pMutex == NULL) || (pCond->condValid != PTHREAD_VALID_OBJ) || (pMutex->mutexValid != PTHREAD_VALID_OBJ)) return (EINVAL); COND_INIT_COMPLETE(pCond); /* * make sure that if anyone else is blocked on this condition variable * that the same mutex was used to guard it. Also verify that mutex is * locked and that we are the owner */ if (((pCond->condMutex != NULL) && (pCond->condMutex != pMutex)) || (!(mutexOwner = _pthreadSemOwnerGet (pMutex->mutexSemId))) || (mutexOwner != taskIdSelf())) { return (EINVAL); } else pCond->condMutex = pMutex; pCond->condRefCount++; pCond->condMutex->mutexCondRefCount++; /* * The guard mutex might be using the PTHREAD_PRIO_PROTECT (i.e. ceiling) * protocol. Since this routine is going to release the guard mutex which * will then be acquired by another thread, we must first keep a local * copy of the original thread's priority as recorded by * pthread_mutex_lock() so that we can restore it back in the mutex * structure when the guard mutex is acquired again by this routine (this * is addressing the defect 130350). * * Note: we don't need to know here whether the guard mutex is actually * using the PTHREAD_PRIO_PROTECT protocol. We choose to blindly save * and restore the content of 'mutexSavPriority'. The * pthread_mutex_unlock() routine will know what to do with it. */ callerSavedPrio = pMutex->mutexSavPriority; #ifdef _WRS_CONFIG_SMP if (selfPthreadId) selfPthreadId->cvcond = pCond; /* * From now on allow for immediate cancellation and check whether there * may be pending cancellation requests. */ pthread_testandset_canceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &savtype); /* * Now, let's atomically release the guard mutex and wait for the * condition variable to be signaled by another thread. This is done * using semExchange which gives and then takes 2 different semaphores * atomically. */ if (semExchange (pMutex->mutexSemId, pCond->condSemId, WAIT_FOREVER) == ERROR) { /* * If errno is EINTR the caller was interrupted by a signal. In this * case errno the caller returns '0' indicating a spurious wakeup. */ if (errno != EINTR) { pthread_testandset_canceltype(savtype, NULL); return (EINVAL); /* internal error */ } } #else /* !_WRS_CONFIG_SMP */ TASK_LOCK(); if (semGive(pMutex->mutexSemId) == ERROR) { TASK_UNLOCK(); return (EINVAL); } if (selfPthreadId) selfPthreadId->cvcond = pCond; /* * From now on allow for immediate cancellation and check whether there * may be pending cancellation requests. */ pthread_testandset_canceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &savtype); if (semTake(pCond->condSemId, WAIT_FOREVER) == ERROR) { /* * If errno is EINTR the caller was interrupted by a signal. In this * case the caller returns '0' indicating a spurious wakeup. */ if (errno != EINTR) { TASK_UNLOCK(); pthread_testandset_canceltype (savtype, NULL); return (EINVAL); /* internal error */ } } TASK_UNLOCK(); #endif /* _WRS_CONFIG_SMP */ pthread_testandset_canceltype (savtype, NULL); if (selfPthreadId) selfPthreadId->cvcond = NULL; if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) return (EINVAL); /* * Restore the original content of the guard mutex's 'mutexSavPriority' * field, i.e. the original priority of the thread in case a priority * ceiling protocol is used by the guard mutex (this is part of the fix * for the defect 130350). */ pMutex->mutexSavPriority = callerSavedPrio; /* * Let's decrement the reference counts. If no one is waiting on the * condition variable anymore, then let's unlink it from the guard mutex. */ pCond->condMutex->mutexCondRefCount--; if (--pCond->condRefCount == 0) pCond->condMutex = NULL; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_cond_timedwait - wait for a condition variable with a timeout (POSIX) * * This function atomically releases the mutex and waits for another * thread to signal the condition variable . As with pthread_cond_wait(), * the mutex must be locked by the calling thread when pthread_cond_timedwait() * is called. * * If the condition variable is signalled before the system time reaches the * time specified by , then the mutex is re-acquired and the calling * thread unblocked. * * If the system time reaches or exceeds the time specified by before * the condition is signalled, then the mutex is re-acquired, the thread * unblocked and ETIMEDOUT returned. * * If the calling thread receives a signal while pending on the condition * variable pthread_cond_timedwait() will return zero, indicating a spurious * wakeup. Because of this, it is important for the caller to re-evaluate the * predicate upon return. * * If the calling thread gets cancelled while pending on the condition * variable pthread_cond_timedwait() will also re-acquire the mutex prior to * executing the cancellation cleanup handlers (if any). The mutex will * however be released prior to the thread exiting so that this mutex can be * used by other threads. * * NOTE * The timeout is specified as an absolute value of the system clock in a * structure (see clock_gettime() for more information). This is * different from most VxWorks timeouts which are specified in ticks relative * to the current time. * * RETURNS: zero on success or spurious wakeup; on failure a non-zero error code: * \is * \i EINVAL * \i ETIMEDOUT * \ie * * ERRNO * * SEE ALSO: pthread_condattr_init(), pthread_condattr_destroy(), * pthread_cond_broadcast(), pthread_cond_destroy(), pthread_cond_init(), * pthread_cond_signal(), pthread_cond_wait() */ int pthread_cond_timedwait ( pthread_cond_t * pCond, /* condition variable */ pthread_mutex_t * pMutex, /* pthread mutex */ const struct timespec * pAbstime /* timeout time */ ) { _Vx_ticks64_t nTicks; struct timespec tmp; struct timespec now; TASK_ID mutexOwner; int savtype; int callerSavedPrio; /* original priority of caller saved by pthread_mutex_lock() */ pthreadCB * selfPthreadId = SELF_PTHREAD_ID; if ((pCond == NULL) || (pCond->condValid != PTHREAD_VALID_OBJ)) return (EINVAL); COND_INIT_COMPLETE(pCond); if ((pMutex == NULL) || (pAbstime == NULL) || !TV_VALID(*pAbstime)) { return (EINVAL); } /* * make sure that if anyone else is blocked on this condition variable * that the same mutex was used to guard it. Also verify that mutex is * locked and that we are the owner */ if (((pCond->condMutex != NULL) && (pCond->condMutex != pMutex)) || (!(mutexOwner = _pthreadSemOwnerGet (pMutex->mutexSemId))) || (mutexOwner != taskIdSelf())) { return (EINVAL); } else pCond->condMutex = pMutex; pCond->condMutex->mutexCondRefCount++; pCond->condRefCount++; /* * The guard mutex might be using the PTHREAD_PRIO_PROTECT (i.e. ceiling) * protocol. Since this routine is going to release the guard mutex which * will then be acquired by another thread, we must first keep a local * copy of the original thread's priority as recorded by * pthread_mutex_lock() so that we can restore it back in the mutex * structure when the guard mutex is acquired again by this routine (this * is addressing the defect 130350). * * Note: we don't need to know here whether the guard mutex is actually * using the PTHREAD_PRIO_PROTECT protocol. We choose to blindly save * and restore the content of 'mutexSavPriority'. The * pthread_mutex_unlock() routine will know what to do with it. */ callerSavedPrio = pMutex->mutexSavPriority; /* * From now on allow for immediate cancellation and check whether there * may be pending cancellation requests. */ pthread_testandset_canceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &savtype); /* * convert to number of ticks (relative time) or return if time has * already passed */ if (clock_gettime(CLOCK_REALTIME, &now) != 0) { pthread_testandset_canceltype (savtype, NULL); return (errno); } /* SPR# 118167 - wrong check for "too late" condition */ if (TV_GT(now, *pAbstime)) /* overflow, e.g. too late */ { /* drop and reacquire mutex as per POSIX */ if (semGive(pMutex->mutexSemId) == ERROR) { pthread_testandset_canceltype (savtype, NULL); return (EINVAL); /* internal error */ } if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) { pthread_testandset_canceltype (savtype, NULL); return (EINVAL); /* internal error */ } pthread_testandset_canceltype (savtype, NULL); /* * Restore the original content of the guard mutex's 'mutexSavPriority' * field, i.e. the original priority of the thread in case a priority * ceiling protocol is used by the guard mutex (this is part of the fix * for the defect 130350). */ pMutex->mutexSavPriority = callerSavedPrio; /* * Let's decrement the reference counts. If no one is waiting on the * condition variable anymore, then let's unlink it from the guard * mutex (defect 130210). */ pCond->condMutex->mutexCondRefCount--; if (--pCond->condRefCount == 0) pCond->condMutex = NULL; return (ETIMEDOUT); } TV_SET(tmp, *pAbstime); TV_SUB(tmp, now); /* * The TV_CONVERT_TO_TICK() macro adds an additional tick to make up for * the partial tick lost while executing the call. Because this routine * uses an absolute time it is incorrect to add this tick, so * TV_CONVER_TO_TICK_NO_PARTIAL() is used instead. */ TV_CONVERT_TO_TICK_NO_PARTIAL(nTicks, tmp); #ifdef _WRS_CONFIG_SMP if (selfPthreadId) selfPthreadId->cvcond = pCond; if (semExchange (pMutex->mutexSemId, pCond->condSemId, (_Vx_ticks_t) nTicks) == ERROR) { /* * If errno is EINTR the caller was interrupted by a signal. In this * case the caller returns '0' indicating a spurious wakeup. */ if (errno != EINTR) { if (selfPthreadId) selfPthreadId->cvcond = NULL; pthread_testandset_canceltype(savtype, NULL); if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) { return (EINVAL); } /* * Restore the original content of the guard mutex's * 'mutexSavPriority' field, i.e. the original priority of * the thread in case a priority ceiling protocol is used by * the guard mutex (this is part of the fix * for the defect 130350). */ pMutex->mutexSavPriority = callerSavedPrio; /* * Let's decrement the reference counts. If no one is waiting on the * condition variable anymore, then let's unlink it from the guard * mutex. */ pCond->condMutex->mutexCondRefCount--; if (--pCond->condRefCount == 0) pCond->condMutex = NULL; return (ETIMEDOUT); } } #else /* !_WRS_CONFIG_SMP */ TASK_LOCK(); if (semGive(pMutex->mutexSemId) == ERROR) { TASK_UNLOCK(); pthread_testandset_canceltype (savtype, NULL); return (EINVAL); } if (selfPthreadId) selfPthreadId->cvcond = pCond; if (semTake(pCond->condSemId, (_Vx_ticks_t) nTicks) == ERROR) { /* * If errno is EINTR the caller was interrupted by a signal. In this * case caller returns '0' indicating spurious wakeup. */ if (errno != EINTR) { if (selfPthreadId) selfPthreadId->cvcond = NULL; TASK_UNLOCK(); pthread_testandset_canceltype (savtype, NULL); if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) { return (EINVAL); } /* * Restore the original content of the guard mutex's * 'mutexSavPriority' field, i.e. the original priority of * the thread in case a priority ceiling protocol is used by * the guard mutex (this is part of the fix for the defect 130350). */ pMutex->mutexSavPriority = callerSavedPrio; /* * Let's decrement the reference counts. If no one is waiting on the * condition variable anymore, then let's unlink it from the guard * mutex. */ pCond->condMutex->mutexCondRefCount--; if (--pCond->condRefCount == 0) pCond->condMutex = NULL; return (ETIMEDOUT); } } TASK_UNLOCK(); #endif /* _WRS_CONFIG_SMP */ pthread_testandset_canceltype (savtype, NULL); if (semTake(pMutex->mutexSemId, WAIT_FOREVER) == ERROR) { return (EINVAL); } if (selfPthreadId) selfPthreadId->cvcond = NULL; /* * Restore the original content of the guard mutex's 'mutexSavPriority' * field, i.e. the original priority of the thread in case a priority * ceiling protocol is used by the guard mutex (this is part of the fix * for the defect 130350). */ pMutex->mutexSavPriority = callerSavedPrio; /* * Let's decrement the reference counts. If no one is waiting on the * condition variable anymore, then let's unlink it from the guard mutex. */ pCond->condMutex->mutexCondRefCount--; if (--pCond->condRefCount == 0) pCond->condMutex = NULL; return (_RETURN_PTHREAD_SUCCESS); } /* * Section 13.5 - Thread Scheduling */ /******************************************************************************* * * pthread_attr_setscope - set contention scope for thread attributes (POSIX) * * For VxWorks PTHREAD_SCOPE_SYSTEM is the only supported contention scope. * If the PTHREAD_SCOPE_PROCESS value is passed to this function this will * result in 'ENOTSUP' being returned. * * RETURNS: On success zero; on failure the 'EINVAL' or 'ENOTSUP' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_getscope(), pthread_attr_init() */ int pthread_attr_setscope ( pthread_attr_t * pAttr, /* thread attributes object */ int contentionScope /* new contention scope */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (contentionScope != PTHREAD_SCOPE_PROCESS && contentionScope != PTHREAD_SCOPE_SYSTEM)) { return (EINVAL); } if (contentionScope == PTHREAD_SCOPE_PROCESS) return (ENOTSUP); #if 0 /* * Restriction on VxWorks: no multiprocessor, so all threads * compete for resources, PTHREAD_SCOPE_SYSTEM always in effect */ pAttr->threadAttrContentionscope = contentionScope; #endif return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getscope - get contention scope from thread attributes (POSIX) * * Reads the current contention scope setting from a thread attributes object. * For VxWorks this is always PTHREAD_SCOPE_SYSTEM. If the thread attributes * object is uninitialized then 'EINVAL' will be returned. The contention * scope is returned in the location pointed to by . * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_init(), pthread_attr_setscope() */ int pthread_attr_getscope ( const pthread_attr_t * pAttr, /* thread attributes object */ int * pContentionScope /* contention scope (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); #if 0 *pContentionScope = pAttr->threadAttrContentionscope; #else /* * Restriction on VxWorks: so all threads * compete for resources, PTHREAD_SCOPE_SYSTEM always in effect */ *pContentionScope = PTHREAD_SCOPE_SYSTEM; #endif return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setinheritsched - set inheritsched attribute in thread attribute object (POSIX) * * This routine sets the scheduling inheritance to be used when creating a * thread with the thread attributes object specified by . * * Possible values are: * \is * \i PTHREAD_INHERIT_SCHED * Inherit scheduling parameters from parent thread. * \i PTHREAD_EXPLICIT_SCHED * Use explicitly provided scheduling parameters (i.e. those specified in the * thread attributes object). * \ie * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_getinheritsched(), pthread_attr_init(), * pthread_attr_setschedparam(), pthread_attr_setschedpolicy() */ int pthread_attr_setinheritsched ( pthread_attr_t * pAttr, /* thread attributes object */ int inheritsched /* inheritance mode */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (inheritsched != PTHREAD_INHERIT_SCHED && inheritsched != PTHREAD_EXPLICIT_SCHED)) { return (EINVAL); } pAttr->threadAttrInheritsched = inheritsched; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getinheritsched - get current value if inheritsched attribute in thread attributes object (POSIX) * * This routine gets the scheduling inheritance value from the thread * attributes object . * * Possible values are: * \is * \i PTHREAD_INHERIT_SCHED * Inherit scheduling parameters from parent thread. * \i PTHREAD_EXPLICIT_SCHED * Use explicitly provided scheduling parameters (i.e. those specified in the * thread attributes object). * \ie * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_init(), pthread_attr_getschedparam(), * pthread_attr_getschedpolicy() pthread_attr_setinheritsched() */ int pthread_attr_getinheritsched ( const pthread_attr_t * pAttr, /* thread attributes object */ int * pInheritsched /* inheritance mode (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); *pInheritsched = pAttr->threadAttrInheritsched; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setschedpolicy - set schedpolicy attribute in thread attributes object (POSIX) * * Select the thread scheduling policy. The default scheduling policy is * to inherit the current system setting. Unlike the POSIX model, * scheduling policies under VxWorks are global. If a scheduling policy is * being set explicitly, the PTHREAD_EXPLICIT_SCHED mode must be set (see * pthread_attr_setinheritsched() for information), and the selected scheduling * policy must match the global scheduling policy in place at * the time; failure to do so will result in pthread_create() failing with * the error 'EPERM'. * * POSIX defines the following policies: * \is * \i SCHED_RR * Realtime, round-robin scheduling. * \i SCHED_FIFO * Realtime, first-in first-out scheduling. * \i SCHED_OTHER * Other, active VxWorks native scheduling policy. * \ie * * Although the SCHED_RR and SCHED_FIFO policies can be set when the precaution * described above is respected, using the SCHED_OTHER policy instead is always * ensured to be successful. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_getschedpolicy(), pthread_attr_init(), * pthread_attr_setinheritsched(), pthread_getschedparam(), * pthread_setschedparam(), sched_setscheduler(), sched_getscheduler() */ int pthread_attr_setschedpolicy ( pthread_attr_t * pAttr, /* thread attributes */ int policy /* new policy */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR)) { return (EINVAL); } pAttr->threadAttrSchedpolicy = policy; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getschedpolicy - get schedpolicy attribute from thread attributes object (POSIX) * * This routine returns, via the pointer , the current scheduling * policy in the thread attributes object specified by . Possible values * for VxWorks systems are SCHED_RR, SCHED_FIFO and SCHED_OTHER. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_init(), pthread_attr_setschedpolicy(), * pthread_getschedparam(), pthread_setschedparam(), sched_setscheduler(), * sched_getscheduler() */ int pthread_attr_getschedpolicy ( const pthread_attr_t * pAttr, /* thread attributes */ int * pPolicy /* current policy (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); *pPolicy = pAttr->threadAttrSchedpolicy; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setschedparam - set schedparam attribute in thread attributes object (POSIX) * * Set the scheduling parameters in the thread attributes object . * The scheduling parameters are essentially the thread's priority. Note that * the PTHREAD_EXPLICIT_SCHED mode must be set * (see pthread_attr_setinheritsched() for information) for the priority to take * effect. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_getschedparam(), pthread_attr_init(), * pthread_getschedparam(), pthread_setschedparam(), * pthread_attr_setinheritsched(), sched_getparam(), sched_setparam() */ int pthread_attr_setschedparam ( pthread_attr_t * pAttr, /* thread attributes */ const struct sched_param * pParam /* new parameters */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (pParam == NULL) || !VALID_PRIORITY (pParam->sched_priority)) return (EINVAL); pAttr->threadAttrSchedparam.sched_priority = pParam->sched_priority; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getschedparam - get value of schedparam attribute from thread attributes object (POSIX) * * Return, via the pointer , the current scheduling parameters from the * thread attributes object . * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_init(), pthread_attr_setschedparam(), * pthread_getschedparam(), pthread_setschedparam(), sched_getparam(), * sched_setparam() */ int pthread_attr_getschedparam ( const pthread_attr_t *pAttr, /* thread attributes */ struct sched_param *pParam /* current parameters (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (pParam == NULL)) return (EINVAL); pParam->sched_priority = pAttr->threadAttrSchedparam.sched_priority; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_getschedparam - get value of schedparam attribute from a thread (POSIX) * * This routine reads the current scheduling parameters and policy of the thread * specified by . The information is returned via and * . * * Note that this routine actually always maps the current VxWorks scheduling * policy on one of the two following POSIX scheduling policies: SCHED_FIFO or * SCHED_RR. The SCHED_OTHER policy can therefore never be returned even if it * has been set via pthread_setschedparam(). * * RETURNS: On success zero; on failure the 'ESRCH' error code. * * ERRNO: N/A * * SEE ALSO: pthread_attr_getschedparam() pthread_attr_getschedpolicy() * pthread_attr_setschedparam() pthread_attr_setschedpolicy() * pthread_setschedparam(), sched_getparam(), sched_setparam() */ int pthread_getschedparam ( pthread_t thread, /* thread */ int * pPolicy, /* current policy (out) */ struct sched_param * pParam /* current parameters (out) */ ) { pthreadCB * pThread = (pthreadCB *)thread; BOOL roundRobinOn; WIND_TCB * pTcb; /* * Let's simply validate the targeted pthread. Locking it would be * overkill since we do not need to modify its control block. * There is a possibility that the targeted pthread is canceled or * deleted during or after the validation. However the sched_getparam() * API verifies the task ID for us. */ if (VALID_PTHREAD (pThread, pTcb) == FALSE) return (ESRCH); roundRobinOn = _schedPxKernelIsTimeSlicing (NULL); if (roundRobinOn == TRUE) *pPolicy = SCHED_RR; else *pPolicy = SCHED_FIFO; return (sched_getparam ((pid_t)pTcb, pParam) != 0 ? errno : 0); } /******************************************************************************* * * pthread_setschedparam - dynamically set schedparam attribute for a thread (POSIX) * * This routine will set the scheduling parameters () and policy * () for the thread specified by . * * In VxWorks the scheduling policy is global and not set on a per-thread basis; * if the selected policy is one of SCHED_FIFO or SCHED_RR and this does not * match the current VxWorks scheduling policy then this function will return * an error ('EPERM'). If the parameter is set to SCHED_OTHER, which * always matches the active scheduling policy, only the thread's priority * will be changed. * * RETURNS: On success zero; on failure one of the following non-zero error * codes: 'EPERM', 'ESRCH' (invalid task ID), 'EINVAL' (scheduling priority is * outside valid range) * * ERRNO: N/A * * SEE ALSO: pthread_attr_getschedparam(), pthread_attr_getschedpolicy(), * pthread_attr_setschedparam(), pthread_attr_setschedpolicy(), * pthread_getschedparam(), sched_getparam(), sched_setparam() */ int pthread_setschedparam ( pthread_t thread, /* thread */ int policy, /* new policy */ const struct sched_param * pParam /* new parameters */ ) { pthreadCB * pThread = (pthreadCB *)thread; WIND_TCB * pTcb; /* * Let's simply validate the targeted pthread. Locking it would be * overkill since we do not need to modify its control block. * There is a possibility that the targeted pthread is canceled or * deleted during or after the validation. However the sched_setparam() * and sched_setscheduler() API verify the task ID for us. */ if (VALID_PTHREAD (pThread, pTcb) == FALSE) return (ESRCH); if (policy == SCHED_OTHER) { if (sched_setparam ((pid_t)pTcb, pParam) == ERROR) { if ((errno == EINVAL) || (errno == ESRCH)) return errno; else return (EPERM); } return (_RETURN_PTHREAD_SUCCESS); } /* * For the other policies, let's see whether this matches with the * current VxWorks scheduling policy. */ if (sched_setscheduler((pid_t)pTcb, policy, pParam) == ERROR) { if ((errno == EINVAL) || (errno == ESRCH)) return errno; else return (EPERM); } return (_RETURN_PTHREAD_SUCCESS); } /* * Section 16 - Thread Management */ /******************************************************************************* * * pthread_attr_init - initialize thread attributes object (POSIX) * * DESCRIPTION * * This routine initializes a thread attributes object. If is NULL * then this function will return EINVAL. * * The attributes that are set by default are as follows: * \is * \i 'Stack Address' * NULL - allow the system to allocate the stack. * \i 'Stack Size' * 0 - use the VxWorks taskLib default stack size. * \i 'Detach State' * PTHREAD_CREATE_JOINABLE * \i 'Contention Scope' * PTHREAD_SCOPE_SYSTEM * \i 'Scheduling Inheritance' * PTHREAD_INHERIT_SCHED * \i 'Scheduling Policy' * SCHED_OTHER (i.e. active VxWorks native scheduling policy). * \i 'Scheduling Priority' * Use pthreadLib default priority * \ie * * Note that the scheduling policy and priority values are only used if the * scheduling inheritance mode is changed to PTHREAD_EXPLICIT_SCHED - see * pthread_attr_setinheritsched() for information. * * Additionally, VxWorks-specific attributes are being set as follows: * \is * \i 'Task Name' * NULL - the task name is automatically generated. * \i 'Task Options' * VX_FP_TASK * \ie * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_attr_destroy(), * pthread_attr_getdetachstate(), * pthread_attr_getinheritsched(), * pthread_attr_getschedparam(), * pthread_attr_getschedpolicy(), * pthread_attr_getscope(), * pthread_attr_getstackaddr(), * pthread_attr_getstacksize(), * pthread_attr_setdetachstate(), * pthread_attr_setinheritsched(), * pthread_attr_setschedparam(), * pthread_attr_setschedpolicy(), * pthread_attr_setscope(), * pthread_attr_setstackaddr(), * pthread_attr_setstacksize(), * pthread_attr_setname() (VxWorks extension), * pthread_attr_setopt() (VxWorks extension) * */ int pthread_attr_init ( pthread_attr_t * pAttr /* thread attributes */ ) { if (pAttr == NULL) return (EINVAL); /* not a POSIX error return */ pAttr->threadAttrStackaddr = NULL; pAttr->threadAttrStacksize = ((size_t) 0); pAttr->threadAttrDetachstate = PTHREAD_CREATE_JOINABLE; pAttr->threadAttrContentionscope = PTHREAD_SCOPE_SYSTEM; pAttr->threadAttrInheritsched = PTHREAD_INHERIT_SCHED; pAttr->threadAttrSchedpolicy = SCHED_OTHER; pAttr->threadAttrSchedparam.sched_priority = DEF_PRIORITY; pAttr->threadAttrStatus = PTHREAD_INITIALIZED_OBJ; pAttr->threadAttrName = NULL; pAttr->threadAttrOptions = VX_FP_TASK; pAttr->threadAttrGuardsize = ((size_t) 0); return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_destroy - destroy a thread attributes object (POSIX) * * Destroy the thread attributes object . It should not be re-used until * it has been reinitialized. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_attr_init() */ int pthread_attr_destroy ( pthread_attr_t * pAttr /* thread attributes */ ) { if (pAttr == NULL) return (EINVAL); /* not a POSIX specified error return */ pAttr->threadAttrStatus = PTHREAD_DESTROYED_OBJ; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setopt - set options in thread attribute object * * This non-POSIX routine sets options in the specified thread attributes * object, . This allows for specifying a non-default set of options for * the VxWorks task acting as a thread. Additional options may be applied to * the task once the thread has been created via the taskOptionsSet() API. * * Note that the task options provided through this routine will supersede the * default options otherwise applied at thread creation. * * See for definitions of valid task options. * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed. * * ERRNO: None. * * SEE ALSO: * pthread_attr_getopt(), taskOptionsSet() */ int pthread_attr_setopt ( pthread_attr_t * pAttr, /* thread attributes */ int options /* VxWorks task options */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); pAttr->threadAttrOptions = options; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getopt - get options from thread attribute object * * This non-POSIX routine gets options from the specified thread attributes * object, . To see the options actually applied to the VxWorks task * under thread, use taskOptionsGet(). * * This routine expects the parameter to be a valid storage space. * * See for definitions of task options. * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed * or if is NULL. * * ERRNO: None. * * SEE ALSO: * pthread_attr_setopt(), taskOptionsGet() */ int pthread_attr_getopt ( pthread_attr_t * pAttr, /* thread attributes */ int * pOptions /* VxWorks task options (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (pOptions == NULL)) return (EINVAL); *pOptions = pAttr->threadAttrOptions; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setname - set name in thread attribute object * * This routine sets the name in the specified thread attributes object, . * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed. * * ERRNO: None. * * SEE ALSO: * pthread_attr_getname() */ int pthread_attr_setname ( pthread_attr_t * pAttr, /* thread attributes */ char * name /* VxWorks task name */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); pAttr->threadAttrName = name; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getname - get name of thread attribute object * * This routine gets the name in the specified thread attributes object, . * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed * or if is NULL. * * ERRNO: None. * * SEE ALSO: * pthread_attr_setname() */ int pthread_attr_getname ( pthread_attr_t * pAttr, /* thread attributes */ char ** name /* VxWorks task name (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (name == NULL)) return (EINVAL); if (pAttr->threadAttrName == NULL) *name = ""; else *name = pAttr->threadAttrName; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setstacksize - set stacksize attribute in thread attributes object (POSIX) * * This routine sets the thread stack size (in bytes) in the specified thread * attributes object, . * * The stack address is set using the routine pthread_attr_setstackaddr(). Note * that failure to set the stack size when a stack address is provided will * result in an 'EINVAL' error status returned by pthread_create(). * * RETURNS: 'EINVAL' if the stack size is lower than PTHREAD_STACK_MIN or if * an invalid thread attribute is passed. Zero otherwise. * * ERRNO: None. * * SEE ALSO: * pthread_attr_getstacksize(), * pthread_attr_setstackaddr(), * pthread_attr_init(), * pthread_create() */ int pthread_attr_setstacksize ( pthread_attr_t * pAttr, /* thread attributes */ size_t stacksize /* new stack size */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); if (stacksize < PTHREAD_STACK_MIN) return (EINVAL); pAttr->threadAttrStacksize = stacksize; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getstacksize - get stack value of stacksize attribute from thread attributes object (POSIX) * * This routine gets the current stack size from the thread attributes object * and places it in the location pointed to by . * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed * or if is NULL. * * ERRNO: None. * * SEE ALSO: * pthread_attr_init(), * pthread_attr_setstacksize(), * pthread_attr_getstackaddr() */ int pthread_attr_getstacksize ( const pthread_attr_t * pAttr, /* thread attributes */ size_t * pStacksize /* current stack size (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (pStacksize == NULL)) return (EINVAL); *pStacksize = pAttr->threadAttrStacksize; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setstackaddr - set stackaddr attribute in thread attributes object (POSIX) * * This routine sets the stack address in the thread attributes object * to be . On VxWorks this address must be the lowest address of * the stack regardless of what the thread considers as the stack base or the * stack end. * * No alignment constraints are imposed by the pthread library so the thread's * stack can be obtained via a simple call to malloc() or memPartAlloc(). * * The memory area used a stack is not automatically freed when the thread * exits. This operation cannot be done via the exiting thread's cleanup stack * since the cleanup handler routines use the same stack as the thread. * Therefore freeing the stack space must be done by the code which allocated * the thread's stack once the thread's task no longer exists in the system. * * The stack size is set using the routine pthread_attr_setstacksize(). Note that * failure to set the stack size when a stack address is provided will result * in an 'EINVAL' error status returned by pthread_create(). * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed. * * ERRNO: None. * * SEE ALSO: * pthread_attr_getstacksize(), * pthread_attr_setstacksize(), * pthread_attr_init() * * INTERNAL * This routine only sets the address of the execution stack. */ int pthread_attr_setstackaddr ( pthread_attr_t * pAttr, /* thread attributes */ void * pStackaddr /* new stack address */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); pAttr->threadAttrStackaddr = pStackaddr; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getstackaddr - get value of stackaddr attribute from thread attributes object (POSIX) * * This routine returns the stack address from the thread attributes object * in the location pointed to by . * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed * or if is NULL. * * ERRNO: None. * * SEE ALSO: * pthread_attr_init(), * pthread_attr_getstacksize(), * pthread_attr_setstackaddr() */ int pthread_attr_getstackaddr ( const pthread_attr_t * pAttr, /* thread attributes */ void ** ppStackaddr /* current stack address (out)*/ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (ppStackaddr == NULL)) return (EINVAL); *ppStackaddr = pAttr->threadAttrStackaddr; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_setdetachstate - set detachstate attribute in thread attributes object (POSIX) * * This routine sets the detach state in the thread attributes object . * The new detach state specified by must be one of * PTHREAD_CREATE_DETACHED or PTHREAD_CREATE_JOINABLE. Any other values will * cause an error to be returned ('EINVAL'). * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_attr_getdetachstate(), * pthread_attr_init() */ int pthread_attr_setdetachstate ( pthread_attr_t * pAttr, /* thread attributes */ int detachstate /* new detach state */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ)) return (EINVAL); if (detachstate != PTHREAD_CREATE_DETACHED && detachstate != PTHREAD_CREATE_JOINABLE) { return (EINVAL); } pAttr->threadAttrDetachstate = detachstate; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_attr_getdetachstate - get value of detachstate attribute from thread attributes object (POSIX) * * This routine returns the current detach state specified in the thread * attributes object . The value is stored in the location pointed to * by . Possible values for the detach state are: * PTHREAD_CREATE_DETACHED and 'PTHREAD_CREATE_JOINABLE'. * * RETURNS: zero on success, 'EINVAL' if an invalid thread attribute is passed * or if is NULL. * * ERRNO: None. * * SEE ALSO: * pthread_attr_init(), * pthread_attr_setdetachstate() */ int pthread_attr_getdetachstate ( const pthread_attr_t * pAttr, /* thread attributes */ int * pDetachstate /* current detach state (out) */ ) { if ((pAttr == NULL) || (pAttr->threadAttrStatus != PTHREAD_INITIALIZED_OBJ) || (pDetachstate == NULL)) return (EINVAL); *pDetachstate = pAttr->threadAttrDetachstate; return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_create - create a thread (POSIX) * * This routine creates a new thread and if successful writes its ID into the * location pointed to by . If is NULL then default attributes * are used. The new thread executes with as its argument. * * The new thread's cancelability state and cancelability type are respectively * set to PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DEFERRED. * * RETURNS: On success zero; on failure one of the following non-zero error * codes: * \is * \i EINVAL * can be returned when the value specified by is invalid, when a * user-supplied stack address is provided but the stack size is invalid, and * when the parameter is null. * \i EAGAIN * can be returned when not enough memory is available to either create the * thread or create a resource required for the thread. * \i EPERM * the explicit scheduling policy does not match the VxWorks scheduling policy * currently in effect. * \ie * * ERRNO: N/A * * SEE ALSO: * pthread_exit(), * pthread_join(), * pthread_detach() * * \INTERNAL: * This routine must take care of not involving anything that cannot be safely * used by a native VxWorks task, and must take care of not turning a native * VxWorks task into a POSIX thread. */ int pthread_create ( pthread_t * pThread, /* Thread ID (out) */ const pthread_attr_t * pAttr, /* Thread attributes object */ void * (*startRoutine)(void *), /* Entry function */ void * arg /* Entry function argument */ ) { return (_pthreadCreate (pThread, pAttr, (void * (*)(void *)) wrapperFunc, startRoutine, arg, (int) posixPriorityNumbering)); } /******************************************************************************* * * pthread_detach - dynamically detach a thread (POSIX) * * This routine puts the thread into the detached state. This prevents * other threads from synchronizing on the termination of the thread using * pthread_join(). * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i ESRCH * \ie * * ERRNO: N/A * * SEE ALSO: pthread_join() */ int pthread_detach ( pthread_t thread /* thread to detach */ ) { pthreadCB * pThread = (pthreadCB *) thread; pthreadCB * selfPthreadId = NULL; int ret; /* Get the ID of the caller; make the caller a POSIX thread if needed */ selfPthreadId = _pthread_id_self(); /* * We don't want the thread's flags to be changed or read while we * manipulate them, so let's lock the thread's control block. */ if (PTHREAD_VALIDATE_AND_LOCK (pThread, selfPthreadId) != OK) return (ESRCH); if (pThread->flags & JOINABLE) { #if DETACHED_PTHREADS_MAY_BE_JOINED pThread->flags |= DETACHED; #else pThread->flags &= ~JOINABLE; #endif ret = _RETURN_PTHREAD_SUCCESS; } else ret = EINVAL; PTHREAD_VALIDATED_UNLOCK (pThread, selfPthreadId); return (ret); } /******************************************************************************* * * pthread_join - wait for a thread to terminate (POSIX) * * This routine will block the calling thread until the thread specified by * terminates, or is canceled. The thread must be in the joinable * state, i.e. it cannot have been detached by a call to pthread_detach(), or * created in the detached state. * * If is not NULL and pthread_join() returns successfully, when * terminates its exit status will be stored in the specified location. * The exit status will be either the value passed to pthread_exit(), or * PTHREAD_CANCELED if the thread was canceled or the thread was deleted by a * VxWorks task. * * Only one thread can wait for the termination of a given thread. If another * thread is already waiting when this function is called an error will be * returned ('EINVAL'). * * If the calling thread passes its own ID in , the call will fail * with the error 'EDEADLK'. * * If the joined thread no longer exists or is about to exit, the error 'ESRCH' * is returned. * * NOTE * All threads that remain at the time they exit should ensure that * pthread_join() is called on their behalf by another thread to reclaim the * resources that they hold. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i ESRCH * \i EDEADLK * \ie * * ERRNO: N/A * * SEE ALSO: pthread_detach(), pthread_exit() */ int pthread_join ( pthread_t thread, /* thread to wait for */ void ** ppStatus /* exit status of thread (out) */ ) { pthreadCB * pThread = (pthreadCB *) thread; pthreadCB * selfPthreadId = _pthread_id_self(); int prevCnclType; /* thread's previous cancel. type */ TASK_ID thTaskId; /* ID of Vx task behind the thread */ /* * Switch to deferred cancelability type as we do not want to be * canceled after having locked the joined pthread's control block. */ _pthread_setcanceltype_inline (PTHREAD_CANCEL_DEFERRED, &prevCnclType, selfPthreadId); /* * We have to acquire the library's mutex prior to locking the caller in * case somebody else is attempting to validate and lock the * about-to-be-joined pthread. */ if (semTake (pthreadLibSemId, WAIT_FOREVER) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ /* * pthread_join() may not use the VALID_PTHREAD macro since one is allowed * to call this routine for a thread that has already exited (therefore the * thread ID stored in the TCB has been reset to NULL). However we still * have to release the thread's resources then and return its exit status * to the caller. */ if (!(pThread && ((pThread->flags & VALID) == VALID))) { (void) semGive (pthreadLibSemId); pthread_testandset_canceltype (prevCnclType, NULL); return ESRCH; } /* Also a thread cannot wait for its own termination... */ if (pThread == selfPthreadId) { (void) semGive (pthreadLibSemId); pthread_testandset_canceltype (prevCnclType, NULL); return (EDEADLK); } /* * Let's lock the joined thread so that it does not go away while we are * preparing to wait for it. We check for a possible error status coming * back from the lock operation as we might be attempting to join a * thread that is already too far gone into its exit sequence to be * joined. */ selfPthreadId->lockedPthread = pThread; if (semExchange (pthreadLibSemId, pThread->lock, WAIT_FOREVER) == ERROR) { selfPthreadId->lockedPthread = 0; pthread_testandset_canceltype (prevCnclType, NULL); return ESRCH; } /* * If the thread has been detached or already has someone waiting for * its termination, then it may not be joined by the caller thread. */ if (!(pThread->flags & JOINABLE) || pThread->flags & JOINER_WAITING) { PTHREAD_UNLOCK(pThread); selfPthreadId->lockedPthread = 0; pthread_testandset_canceltype (prevCnclType, NULL); return (EINVAL); } /* * If the thread has already exited and was joinable, provide its exit * status to the caller, release the control block on its behalf, and * return success. */ #if DETACHED_PTHREADS_MAY_BE_JOINED if ((pThread->flags & JOINABLE) && !(pThread->flags & DETACHED) && (pThread->flags & TASK_EXITED)) #else if ((pThread->flags & JOINABLE) && (pThread->flags & TASK_EXITED)) #endif { if (ppStatus != NULL) *ppStatus = pThread->exitStatus; /* * No call to PTHREAD_UNLOCK is necessary since the lock mutex is * going to be destroyed. */ pthreadCtrlBlockFree (pThread); selfPthreadId->lockedPthread = 0; pthread_testandset_canceltype (prevCnclType, NULL); return (_RETURN_PTHREAD_SUCCESS); } /* * Let's indicate that someone is waiting for the thread's termination * and let's register which pthread the calling pthread has joined (in * case of cancellation of the caller). */ pThread->flags |= JOINER_WAITING; selfPthreadId->joinedPthread = pThread; /* let's not depend on the thread data */ thTaskId = PTHREAD_TO_NATIVE_TASK (pThread); PTHREAD_UNLOCK(pThread); selfPthreadId->lockedPthread = 0; /* * From now on allow for immediate cancellation and let's check whether * there are pending cancellation requests. * Note: if the caller is cancelled at this point this leads to a memory * leak in case nobody else joins the targeted pthread. * This is an unclear area of the POSIX.1 specs: on the one hand * POSIX.1 states that pthread_join() should be called with the * caller's cancelability type set to PTHREAD_CANCEL_DEFERRED, but * on the other hand it lists pthread_join() as a compulsory * cancellation point. */ pthread_testandset_canceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* Then let's wait for the thread's termination */ if (semTake (pThread->exitJoinSemId, WAIT_FOREVER) != OK) return EINVAL; /* POSIX in shape but not in spirit... */ /* Re-instate the calling thread's previous cancelability type */ _pthread_setcanceltype_inline (prevCnclType, NULL, selfPthreadId); if (ppStatus != NULL) *ppStatus = pThread->exitStatus; /* * Re-acquire the exited pthread's lock. Any potential concurrent joiner * should release the lock quickly since the JOINER_WAITING flag has been * set earlier. The pthread's lock is never unlocked after that point * since the mutex is going to be deleted. * * Note: the order of the acquisition of the pthread's lock and the * library's mutex is reversed here; this is not a problem since the use * of semExchange() guarantees that no deadlocks can occur. */ PTHREAD_LOCK (pThread); /* Free the joined pthread's control block */ pthreadCtrlBlockFree(pThread); selfPthreadId->joinedPthread = NULL; /* * Perform a wait on the VxWorks task and continue only when the * task is considered removed from the system. */ if (taskWait (thTaskId, WAIT_FOREVER) == ERROR) { if (errno != S_objLib_OBJ_ID_ERROR) { /* * if errno == S_objLib_OBJ_ID_ERROR assume thread is already * gone and continue. */ return (EINVAL); /* POSIX in shape but not in spirit... */ } } return (_RETURN_PTHREAD_SUCCESS); } /******************************************************************************* * * pthread_exit - terminate a thread (POSIX) * * This function terminates the calling thread. All cleanup handlers that have * been set for the calling thread with pthread_cleanup_push() are executed * in reverse order (the most recently added handler is executed first). * Termination functions for thread-specific data are then called for all * keys that have non-NULL values associated with them in the calling thread * (see pthread_key_create() for more details). Finally, execution of the * calling thread is stopped. * * The argument is the return value of the thread and can be * consulted from another thread using pthread_join() unless this thread was * detached (i.e. a call to pthread_detach() had been made for it, or it was * created in the detached state). * * All threads that remain at the time they exit should ensure that * pthread_join() is called on their behalf by another thread to reclaim the * resources that they hold. * * If the calling task is not a POSIX thread, it will exit immediately. * * RETURNS: Does not return. * * ERRNO: N/A * * SEE ALSO: * pthread_cleanup_push(), * pthread_detach(), * pthread_join(), * pthread_key_create() */ void pthread_exit ( void * status /* exit status */ ) { WIND_TCB * pTcb = (WIND_TCB *)taskIdSelf(); pthreadCB * pThread = NULL; /* Get the caller's pthread control block address */ if ((pThread = (pthreadCB *)pTcb->pPthread) == NULL) exit (EXIT_FAILURE); /* not a POSIX thread */ /* * Change the thread's cancelability type and state to prevent * any possibility for cancellation. This ensures that no deadlock occurs * while the library's mutex or the pthread's control block's mutex are * acquired. this also ensures that the cleanup handlers can be safe from * triggering a cancellation in case they happen to be or call cancellation * points. * Note that there is really no reason to check whether the calling * pthread is still valid... */ pThread->canceltype = PTHREAD_CANCEL_DEFERRED; pThread->cancelstate = PTHREAD_CANCEL_DISABLE; /* * Let's not go away just yet if somebody is attempting to do something * on the calling pthread. We have to acquire the library's mutex * prior to locking the caller in case somebody else is attempting to * validate and lock the about-to-exit pthread * Note also that there is no way to handle semTake() or semGive() failure * here... */ (void)semTake (pthreadLibSemId, WAIT_FOREVER); (void)semExchange (pthreadLibSemId, pThread->lock, WAIT_FOREVER); /* * Execute the bulk of the exit sequence: cleanup operations, resource * freeing operations. */ _pthread_onexit_cleanup (pThread, FALSE); /* * If the thread is not joinable let's do the resource clean up now then * let the underlying VxWorks task exit. */ #if DETACHED_PTHREADS_MAY_BE_JOINED if (!(pThread->flags & JOINABLE) || ((pThread->flags & DETACHED) && !(pThread->flags & JOINER_WAITING))) #else if (!(pThread->flags & JOINABLE)) #endif { /* * Note: the exiting thread is not unlocked since the mutex is going to * be deleted by pthreadCtrlBlockFree(). The resulting error coming from * pthreadValidateAndLock() will tell any thread pended for an operation * on this exiting thread that this operation cannot proceed. */ pthreadCtrlBlockFree(pThread); /* * We must reset the pthread's ID to NULL in the TCB so that the pthread * delete hook does not attempt to release resources already released * when it kicks in. */ pTcb->pPthread = NULL; taskExit ((_Vx_exit_code_t) status); /* Never comes here */ } /* * The thread should have someone waiting for it, let pthread_join() do * the resource cleaning work. * Note that if for some reason the thread is joinable but no one joined * it then the pthread data structure will remain allocated, generating a * memory leak. This seems to be a known issue with POSIX threads ("shadow * threads"). Also, although the POSIX standard does not say explicitly * that pthread_join() or pthread_detach() must be called after a thread is * created, its rationale section does say that one of these two routines * "should eventually be called for every thread that is created so that * the storage associated with the thread may be reclaimed". */ pThread->exitStatus = status; /* * Release joining thread if any. Note that the pthread may be unlocked * only _after_ the JOINER_WAITING flag has been checked otherwise it * would be possible for the joiner thread to free the exiting thread's * control block _before_ the exiting thread has had the time to verify * the flags. Also it is not possible to unlock the thread after the * joiner thread has been released since the exiting thread's control * block might have already been freed by the joiner thread when the * exiting thread comes to unlocking itself... */ if (pThread->flags & JOINER_WAITING) { PTHREAD_UNLOCK (pThread); (void) semGive (pThread->exitJoinSemId); } else PTHREAD_UNLOCK (pThread); /* * We must reset the pthread's ID to NULL in the TCB so that the pthread * delete hook does not attempt to release resources already released * when it kicks in. */ pTcb->pPthread = NULL; taskExit ((_Vx_exit_code_t) status); /* Never returns */ } /******************************************************************************* * * pthread_equal - compare thread IDs (POSIX) * * Tests the equality of the two threads and . * * RETURNS: Non-zero if and refer to the same thread, otherwise zero. */ int pthread_equal ( pthread_t t1, /* thread one */ pthread_t t2 /* thread two */ ) { return (t1 == t2); } /******************************************************************************* * * pthread_self - get the calling thread's ID (POSIX) * * This function returns the calling thread's ID. * * If the caller is a native VxWorks task it will be given a POSIX thread * persona. * * * RETURNS: Calling thread's ID. */ pthread_t pthread_self (void) { WIND_TCB * pTcb = (WIND_TCB *)taskIdSelf(); /* * In case the caller is a native VxWorks task let's give it a thread * persona. */ self_become_pthread(); return ((pthread_t)pTcb->pPthread); } /******************************************************************************* * * pthread_once - dynamic package initialization (POSIX) * * This routine provides a mechanism to ensure that one, and only one call * to a user specified initialization function will occur. This allows all * threads in a system to attempt initialization of some feature they need * to use, without any need for the application to explicitly prevent multiple * calls. * * When a thread makes a call to pthread_once(), the first thread to call it * with the specified control variable, , will result in a call to * , but subsequent calls will not. The parameter * determines whether the associated initialization routine has been called. * The function is complete when pthread_once() returns. * * The function pthread_once() is not a cancellation point; however, if the * function is a cancellation point, and the thread is canceled * while executing it, the effect on is the same as if * pthread_once() had never been called. * * CAVEAT * If the initialization function does not return then all threads calling * pthread_once() with the same control variable will stay blocked as well. * It is therefore imperative that the initialization function always returns. * * WARNING * If has automatic storage duration or is not initialized to * the value PTHREAD_ONCE_INIT, the behavior of pthread_once() is undefined. * * The constant PTHREAD_ONCE_INIT is defined in the 'pthread.h' header file. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: None * * INTERNAL * The usage of semaphores in a two-level fashion is for fear that the user's * initialization function does not return. If we were to use only a * library-level semaphore for the mutual exclusion we would risk blocking all * the threads calling pthread_once() for any control object in case one of the * initialization routine does not return... Therefore the library-level * semaphore is only used to protect the initialization of the control object, * while the release of the created resource is protected via taskLock() (see * below) since POSIX does not provide a cleaning API for this purpose. The * uniqueness of the call to the initialization function is done by an other * semaphore created when the control object is initialized and acting as a * synchronization mean. This semaphore is deleted once the initialization * function has run to completion. The seemingly redundant flag verifications * are required to prevent race conditions. */ int pthread_once ( pthread_once_t * pOnceControl, /* once control location */ void (*initFunc)(void) /* function to call */ ) { pthreadCB * pThread; if (initFunc == NULL) return EINVAL; /* agree with IEEE 1003.1-2003 */ if (pOnceControl == NULL) return (EINVAL); startAgain: /* * Let's return immediately if the initialization function has already * been called for this control object. */ if (pOnceControl->onceDone) return (_RETURN_PTHREAD_SUCCESS); if (semTake (pthreadLibOnceSemId, WAIT_FOREVER) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ /* * If we come here, the call has not been done yet (or is not yet finished) * or the control object hasn't been initialized yet. Let's check all this. */ if (!pOnceControl->onceInitialized) { /* * If we come here, no one else has initialized this control * object yet. Let's do it. */ if ((pOnceControl->onceMutex = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY)) == SEM_ID_NULL) { (void)semGive (pthreadLibOnceSemId); return EINVAL; /* POSIX in shape but not in spirit... */ } /* * Let's flag the control object as initialized and let's * register who initialized it in order to prevent a possible * recursive call to pthread_once() with the same control object * by the initialization function (SPR #98589). */ pOnceControl->onceInitialized = TRUE; pOnceControl->onceMyTid = taskIdSelf(); /* * The field storing whether we will be cancelled in the init * routine is setup here. */ pOnceControl->onceCanceled = FALSE; if (semGive (pthreadLibOnceSemId) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ /* * We also store the address of the control variable in our thread's * private data so that we can do the necessary cleanup if we * are canceled in the initialization routine. Magic spell * summoning is needed however before we can access the thread * internals. This will make the caller a POSIX thread if needed. */ pThread = _pthread_id_self(); pThread->pOnceControl = pOnceControl; /* * We are now ready to call the initialization function. Once it has * returned the 'onceDone' state variable is immediately set to TRUE. * XXX PAD - this still leaves the possibility of a cancellation * happening just before the state variable is set. This * makes possible for the initialization function to * execute more than once... */ initFunc (); pOnceControl->onceDone = TRUE; /* * Make ready to unblock any concurrent threads that might be * waiting for the initialization routine to be executed and then * delete the onceMutex semaphore since it is not required * anymore. The flushing operation is done under the protection * of a semaphore in order to make sure that no late incoming * concurrent thread might "miss the train" and call semTake() * with a NULL semaphore ID. */ if (semTake (pthreadLibOnceSemId, WAIT_FOREVER) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ /* * Let's unblock all possible pending threads on this control * object. */ (void)semFlush (pOnceControl->onceMutex); /* * Now delete the semaphore since it is no longer needed. Like * the creation of the object, its deletion is protected. */ (void)semDelete (pOnceControl->onceMutex); pOnceControl->onceMutex = NULL; if (semGive (pthreadLibOnceSemId) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ /* * We have to wait till everything is settled before resetting the * pthread's reference to the control variable to NULL. This way, in * case the pthread is cancelled _after_ the initialization routine * has been called, we give a chance to pthread_exit() to simply flush * the pending pthreads instead of completely resetting the control * variable (which would make possible for the the initialization * routine to be executed more than once). */ pThread->pOnceControl = NULL; return (_RETURN_PTHREAD_SUCCESS); } /* * The control object has been initialized by someone else. Let's * make sure of that and check that the call to pthread_once() has not * been made by the initialization routine itself using the same * control object in a recursive fashion (SPR #98589). If it is the * case we just return now. */ if (pOnceControl->onceMyTid == taskIdSelf ()) { if (semGive (pthreadLibOnceSemId) != OK) return EINVAL; /* POSIX in shape but not in spirit... */ return (_RETURN_PTHREAD_SUCCESS); } /* Let's check whether the initialization function has returned already */ if (pOnceControl->onceDone) { if (semGive (pthreadLibOnceSemId) != OK) return EINVAL; /* POSIX in shape but not in spirit... */ return (_RETURN_PTHREAD_SUCCESS); } /* * The initialization function appears to have not returned yet. Let's wait * for it by pending on the once control's mutex. * Note that, since the semaphore associated to the control object will * be deleted after the initialization function has run to completion, we * need to make sure this semaphore is still valid before it is taken * (semTake() will crash if it is passed a null semaphore ID...). * The verification and the count of pended concurrent threads is also * protected by a mutex so that no thread should pass the verification of * the mutex ID while the first thread is already preparing to flush and * delete the said mutex. */ /* * Let's pend on the once control variable's mutex till it is flushed. * * CAVEAT: the caller may be blocked forever if the initialization * function does not return. If the init routine was cancelled however, * there is no problem (pthreadOnce returned to a pristine state). */ /* coverity[var_deref_model : FALSE] */ if (semExchange (pthreadLibOnceSemId, pOnceControl->onceMutex, WAIT_FOREVER) == ERROR) { /* * Just to be safe, let's check whether semTake() returned with * an error because the semaphore has been destroyed by another * thread executing pthread_once(). If this is the case then the * onceMutex member of the control object will be null and we can * safely continue since the initialization function has been * executed then. Otherwise, it is possible there is a real problem * and we need to report it. */ if (pOnceControl->onceMutex != NULL) return EINVAL; /* POSIX in shape but not in spirit... */ } /* * POSIX does not specifies what happens for other threads waiting for * the initialization routine to complete when it was canceled; we chose * to make them return to square one and redo the initialization step * again. This will also restore the onceCanceled field to a pristine * state. */ if (pOnceControl->onceCanceled) goto startAgain; /* The initialization function has returned. Let's continue */ return (_RETURN_PTHREAD_SUCCESS); } /* * Section 17 - Thread-Specific Data */ /******************************************************************************* * * pthread_key_create - create a thread specific data key (POSIX) * * This routine allocates a new thread specific data key. The key is stored in * the location pointed to by . The value initially associated with the * returned key is NULL in all currently executing threads. If the maximum * number of keys are already allocated, the function returns an error * ('EAGAIN'). * * The parameter specifies a destructor function associated with * the key. When a thread terminates via pthread_exit(), or by cancellation, * is called with the value associated with the key in that * thread as an argument. The destructor function is 'not' called if that value * is NULL. The order in which destructor functions are called at thread * termination time is unspecified. * * It is the user's responsibility to call pthread_key_delete() when the memory * associated with the key is no longer required, and to ensure that no threads * access the key after it has been deleted. Failure to do this can return * unexpected results, and can cause memory leaks. * * RETURNS: On success zero; on failure the 'EAGAIN' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_getspecific(), * pthread_key_delete(), * pthread_setspecific() */ int pthread_key_create ( pthread_key_t * pKey, /* thread specific data key */ void (*destructor)(void *) /* destructor function */ ) { self_become_pthread (); (void)semTake (key_mutex, WAIT_FOREVER); /* can't return on error */ for ((*pKey) = 0; (*pKey) < _POSIX_THREAD_KEYS_MAX; (*pKey)++) { if (key_table[(*pKey)].keyStatus == KEY_FREE) { /* * Coverity complains that chosen slot's mutex * should be held here while accessing the other * slot members (particularly 'destructor'). * However, this is the code that initializes * the key slot, including the mutex. It is * undefined behavior if a thread uses the key * (e.g. pthread_setspecific() or pthread_getspecific) * before the key slot is initialized, and it * is legitimate to initialize the other members * before the mutex. */ /* coverity[missing_lock : FALSE] */ key_table[(*pKey)].keyStatus = KEY_CREATED; /* coverity[missing_lock : FALSE] */ key_table[(*pKey)].destructor = destructor; (void)pthread_mutex_init(&(key_table[(*pKey)].mutex), NULL); (void)semGive (key_mutex); return (_RETURN_PTHREAD_SUCCESS); } } (void)semGive (key_mutex); return(EAGAIN); } /******************************************************************************* * * pthread_setspecific - set thread specific data (POSIX) * * Sets the value of the thread specific data associated with to * for the calling thread. * * Setting the to NULL will cause the destructor not to be called during * cleanup of the thread. * * RETURNS: On success zero; on failure a non-zero error code: * \is * \i EINVAL * \i ENOMEM * \ie * * ERRNO: N/A * * SEE ALSO: * pthread_getspecific(), * pthread_key_create(), * pthread_key_delete() */ int pthread_setspecific ( pthread_key_t key, /* thread specific data key */ const void * value /* new value */ ) { int ret; pthreadCB * pThread; /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); if ((pThread->privateData) || (pThread->privateData = pthread_key_allocate_data())) { if ((key < _POSIX_THREAD_KEYS_MAX) && (key_table[key].keyStatus != KEY_FREE)) { int res = pthread_mutex_lock(&(key_table[key].mutex)); if (res != 0) return res; if (pThread->privateData[key] == NULL) { if (value != NULL) { pThread->privateDataCount++; key_table[key].keyStatus = KEY_IN_USE; } } else { if (value == NULL) { /* * Decrement the data count here because during cleanup we * do not want to call the destructor for this associated * key since the value is NULL. */ pThread->privateDataCount--; } } if (key_table[key].keyStatus != KEY_FREE) { key_table[key].keyStatus = KEY_IN_USE; } pThread->privateData[key] = value; ret = OK; (void)pthread_mutex_unlock(&(key_table[key].mutex)); } else { ret = EINVAL; } } else { ret = ENOMEM; } return(ret); } /******************************************************************************* * * pthread_getspecific - get thread specific data (POSIX) * * This routine returns the value associated with the thread specific data * key for the calling thread. * * RETURNS: The value associated with , or NULL. * * ERRNO: N/A * * SEE ALSO: * pthread_key_create(), * pthread_key_delete(), * pthread_setspecific() */ void * pthread_getspecific ( pthread_key_t key /* thread specific data key */ ) { void * ret = NULL; pthreadCB * pThread; /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); if ((pThread->privateData) && (key < _POSIX_THREAD_KEYS_MAX)) { if (0 == pthread_mutex_lock(&(key_table[key].mutex))) { if (key_table[key].keyStatus == KEY_IN_USE) { ret = (void *)pThread->privateData[key]; } (void)pthread_mutex_unlock(&(key_table[key].mutex)); } } return(ret); } /******************************************************************************* * * pthread_key_delete - delete a thread specific data key (POSIX) * * This routine deletes the thread specific data associated with , and * deallocates the key itself. It does not call any destructor associated * with the key. * * Any attempt to use key following the call to pthread_key_delete() results * in undefined behavior. * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_key_create() */ int pthread_key_delete ( pthread_key_t key /* thread specific data key to delete */ ) { if (key < _POSIX_THREAD_KEYS_MAX) { if (semTake (key_mutex, WAIT_FOREVER) == ERROR) return EINVAL; /* POSIX in shape but not in spirit... */ switch (key_table[key].keyStatus) { case KEY_IN_USE: case KEY_CREATED: /* * For safety, the mutex is acquired prior to being destroyed * This is an unspecified behavior for POSIX but is supported * technique on VxWorks */ (void)pthread_mutex_lock (&(key_table[key].mutex)); key_table[key].destructor = NULL; key_table[key].keyStatus = KEY_FREE; /* coverity[sleep] */ (void)pthread_mutex_destroy (&(key_table[key].mutex)); (void)semGive (key_mutex); return (_RETURN_PTHREAD_SUCCESS); case KEY_FREE: (void)semGive (key_mutex); return (EINVAL); default: (void)semGive (key_mutex); return (EBUSY); } } else { return (EINVAL); } } /* * Section 18 - Thread Cancellation */ /******************************************************************************* * * pthread_cancel - cancel execution of a thread (POSIX) * * This routine sends a cancellation request to the thread specified by * . Depending on the settings of that thread, it may ignore the * request, terminate immediately or defer termination until it reaches a * cancellation point. * * When the thread terminates it performs as if pthread_exit() had been called * with the exit status 'PTHREAD_CANCELED'. * * See also the list of cancellation points in system calls and library calls * detailed in the pthreadLib documentation. * * IMPLEMENTATION NOTE * In VxWorks, asynchronous thread cancellation is accomplished using a signal. * The signal 'SIGCNCL' has been reserved for this purpose. Applications should * take care not to block or handle this signal. * * Please also note that all threads that remain at the time they are * cancelled should ensure that pthread_join() is called on their behalf by * another thread to reclaim the resources that they hold. * * RETURNS: On success zero; on failure the 'ESRCH' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_exit(), * pthread_setcancelstate(), * pthread_setcanceltype(), * pthread_testcancel() */ int pthread_cancel ( pthread_t thread /* thread to cancel */ ) { pthreadCB * pThread = (pthreadCB *)thread; pthreadCB * selfPthreadId = NULL; int status = _RETURN_PTHREAD_SUCCESS; /* Get the ID of the caller; make the caller a POSIX thread if needed */ selfPthreadId = _pthread_id_self(); /* * We validate and lock the to-be-cancelled pthread so that it cannot go * away while we check its cancellation properties. */ if (PTHREAD_VALIDATE_AND_LOCK (pThread, selfPthreadId) != OK) return (ESRCH); /* * POSIX mandates that cancellation requests are held pending for threads * which are in the PTHREAD_CANCEL_DISABLE state (SPR#114130) as well as * for threads whose cancellability is enabled but which are in the * PTHREAD_CANCEL_DEFERRED state. * This means that unless the thread is enabled and asynchronous (in which * case we act immediately), cancellation requests are held pending. */ if (pThread->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS && pThread->cancelstate == PTHREAD_CANCEL_ENABLE) { /* * Release pthread's control block now or pthread_exit() would block. */ PTHREAD_VALIDATED_UNLOCK (pThread, selfPthreadId); status = pthread_kill ((pthread_t)pThread, SIGCANCEL); } else { pThread->cancelrequest = 1; PTHREAD_VALIDATED_UNLOCK (pThread, selfPthreadId); } return status; } /******************************************************************************* * * pthread_setcancelstate - set cancellation state for calling thread (POSIX) * * This routine sets the cancellation state for the calling thread to , * and, if is not NULL, returns the old state in the location * pointed to by . * * The state can be one of the following: * \is * \i PTHREAD_CANCEL_ENABLE * Enable thread cancellation. * \i PTHREAD_CANCEL_DISABLE * Disable thread cancellation (i.e. thread cancellation requests are ignored). * \ie * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_cancel(), * pthread_setcanceltype(), * pthread_testcancel() */ int pthread_setcancelstate ( int state, /* new state */ int * oldstate /* old state (out) */ ) { pthreadCB * pThread; if ((state != PTHREAD_CANCEL_DISABLE) && (state != PTHREAD_CANCEL_ENABLE)) return (EINVAL); /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); /* * Note that no validation and locking of the pthread is necessary since * 1) only the calling pthread executes this code and, 2) the unprotected * setting of the field will not adversely affect a pthread * calling pthread_cancel() at the same time. */ if (oldstate != NULL) *oldstate = pThread->cancelstate; pThread->cancelstate = state; return (0); } /******************************************************************************* * * pthread_setcanceltype - set cancellation type for calling thread (POSIX) * * This routine sets the cancellation type for the calling thread to . * If is not NULL, then the old cancellation type is stored in the * location pointed to by . * * Possible values for are: * \is * \i PTHREAD_CANCEL_ASYNCHRONOUS * Any cancellation request received by this thread will be acted upon as soon * as it is received. * \i PTHREAD_CANCEL_DEFERRED * Cancellation requests received by this thread will be deferred until the * next cancellation point is reached. * \ie * * RETURNS: On success zero; on failure the 'EINVAL' error code. * * ERRNO: N/A * * SEE ALSO: * pthread_cancel(), * pthread_setcancelstate(), * pthread_testcancel() */ int pthread_setcanceltype ( int type, /* new type */ int * oldtype /* old type (out) */ ) { pthreadCB * pThread; if (type != PTHREAD_CANCEL_ASYNCHRONOUS && type != PTHREAD_CANCEL_DEFERRED) { return EINVAL; } /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); return _pthread_setcanceltype_inline (type, oldtype, pThread); } /******************************************************************************* * * pthread_testcancel - create a cancellation point in the calling thread (POSIX) * * This routine creates a cancellation point in the calling thread. It has * no effect if cancellation is disabled (i.e. the cancellation state has been * set to PTHREAD_CANCEL_DISABLE using the pthread_setcancelstate() function). * * If cancellation is enabled, the cancellation type is PTHREAD_CANCEL_DEFERRED * and a cancellation request has been received, then this routine will call * pthread_exit() with the exit status set to 'PTHREAD_CANCELED'. If any of * these conditions is not met, then the routine does nothing. * * RETURNS: N/A * * ERRNO: N/A * * SEE ALSO: * pthread_cancel(), * pthread_setcancelstate(), * pthread_setcanceltype() */ void pthread_testcancel (void) { pthreadCB * pThread; /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); _pthread_testcancel_inline (pThread); } /******************************************************************************* * * pthread_cleanup_push - pushes a routine onto the cleanup stack (POSIX) * * This routine pushes the specified cancellation cleanup handler routine, * , onto the cancellation cleanup stack of the calling thread. When * a thread exits and its cancellation cleanup stack is not empty, the cleanup * handlers are invoked with the argument in LIFO order from the * cancellation cleanup stack. * * RETURNS: N/A * * ERRNO: N/A * * SEE ALSO: * pthread_cleanup_pop(), * pthread_exit() */ void pthread_cleanup_push ( void (*routine)(void *), /* cleanup routine */ void * arg /* argument */ ) { pthreadCB *pThread; cleanupHandler *pClean; /* Get the ID of the caller; make the caller a POSIX thread if needed */ pThread = _pthread_id_self(); pClean = malloc(sizeof (cleanupHandler)); if (pClean == NULL) return; pClean->routine = routine; pClean->arg = arg; pClean->next = pThread->handlerBase; pThread->handlerBase= pClean; } /******************************************************************************* * * pthread_cleanup_pop - pop a cleanup routine off the top of the stack (POSIX) * * This routine removes the cleanup handler routine at the top of the * cancellation cleanup stack of the calling thread and executes it if * is non-zero. The routine should have been added using the * pthread_cleanup_push() function. * * Once the routine is removed from the stack it will no longer be called when * the thread exits. * * RETURNS: N/A * * ERRNO: N/A * * SEE ALSO: * pthread_cleanup_push(), * pthread_exit() */ void pthread_cleanup_pop ( int run /* execute handler? */ ) { pthreadCB *pThread = SELF_PTHREAD_ID; cleanupHandler *pClean; if (pThread == NULL) return; /* not a POSIX thread */ pClean = pThread->handlerBase; if (pClean) { pThread->handlerBase = pClean->next; if (run != 0) pClean->routine(pClean->arg); free(pClean); } } /*############################################################################# # # Support routines section. # #############################################################################*/ /******************************************************************************** * _pthread_oncancel_exit - terminate the calling pthread when canceled * * This routine handles the first stage of the cancellation process (i.e. prior * to the exit sequence actually taking place. It is essentially ensuring that * various aspects of the POSIX specs regarding cancellations are respected. * * Note that this routine is invoked in two different ways: 1) when a pthread * calls pthread_testcancel(), and 2) when a pthread gets asynchronously * cancelled (i.e. the SIGCANCEL signal handler is the caller). * * RETURNS: N/A * * \NOMANUAL */ _WRS_INLINE void _pthread_oncancel_exit ( pthreadCB * pThread /* pointer to canceled pthread's ctrl block */ ) { pthread_cond_t * cvcond; /* * Change the cancelled thread's cancelability type and state to prevent * any possibility for concurrent cancellations. */ pThread->cancelstate = PTHREAD_CANCEL_DISABLE; pThread->canceltype = PTHREAD_CANCEL_DEFERRED; /* * Check whether the outgoing pthread might have locked another pthread. * If that's the case, release this other pthread now so to avoid * deadlocks. * Note that in the cancellation scenario we are assured that the locked * pthread has not exited since both the pthread_cancel() and * pthread_exit() APIs first acquire the pthread's lock before * proceeding. If the outgoing pthread already holds the lock those * operations will be deferred until the lock is released. */ if (pThread->lockedPthread) { PTHREAD_UNLOCK (pThread->lockedPthread); pThread->lockedPthread = NULL; } /* * The POSIX specs mandate that a pthread canceled while joining * another pthread must not leave this other pthread detached (i.e. * yet another pthread should be able to join it and free its resources). */ if (pThread->joinedPthread) { /* * We can't call PTHREAD_VALIDATE_AND_LOCK as the joined pthread * might have exited already. */ (void)semTake (pthreadLibSemId, WAIT_FOREVER); if ((pThread->joinedPthread->flags & VALID) == VALID) { (void)semExchange (pthreadLibSemId, pThread->joinedPthread->lock, WAIT_FOREVER); pThread->joinedPthread->flags &= ~JOINER_WAITING; PTHREAD_UNLOCK (pThread->joinedPthread); } else (void)semGive (pthreadLibSemId); pThread->joinedPthread = NULL; } /* * Now let's ensure that, per the POSIX specs for the pthread_cond_wait() * and pthread_cond_timedwait() API, a thread canceled while blocked on a * cancellation variable re-acquires the guard mutex prior to executing the * cancellation cleanup handlers. * * This routines also makes sure that the guard mutex is not recursively * acquired (i.e. the cancelled pthread already owns it) in case the * cancellation happened before semExchange() could release the guard mutex. * * Note: this execution path happens when the cancellation request is * issued before the thread actually gets to pend (synchronous path). */ cvcond = pThread->cvcond; if (cvcond && cvcond->condMutex) { if (_pthreadSemOwnerGet (cvcond->condMutex->mutexSemId) != taskIdSelf()) (void)pthread_mutex_lock (cvcond->condMutex); /* * Decrement this condition variable's reference count so that * it can be deleted. */ --cvcond->condRefCount; } /* coverity[sleep] */ pthread_exit (PTHREAD_CANCELED); } /******************************************************************************* * * sigCancelHandler - thread cancel signal handler function * * Handles thread cancel signals. It is attached as part of the thread creation. * * \NOMANUAL */ LOCAL void sigCancelHandler ( int dummy /* not used */ ) { pthreadCB * pThread = SELF_PTHREAD_ID; /* can never be NULL as this handler applies to pthreads only */ _pthread_oncancel_exit (pThread); } /******************************************************************************* * * wrapperFunc - thread entry wrapper function * * This function is a wrapper for the thread. It manages the attachment of * the thread cancel signal handler, set the thread's signal mask to that of its * creator, calls the thread's entry routine, and cleans up if the thread entry * routine should just 'return' rather than calling pthread_exit(). * * This routine is invoked by _pthreadCreate() through taskCreate(). * * RETURNS: this routine should never return. * * \NOMANUAL */ LOCAL void wrapperFunc ( void * (*function)(void *), /* thread's entry routine */ void * arg, /* thread's argument */ const sigset_t creatorSigMask /* thread's creator signal mask */ ) { /* Register the cancellation signal handler */ signal (SIGCANCEL, sigCancelHandler); /* * POSIX 1003.1 states that the new thread inherits the signal * mask of its creator. Set the mask now. */ (void) sigprocmask (SIG_SETMASK, &creatorSigMask, NULL); /* * Call the thread's entry routine here, and pass its result to * pthread_exit(). */ pthread_exit((void *)(function)(arg)); /* NOTREACHED */ return; } /******************************************************************************** * _pthread_testcancel_inline - support routine for pthread_testcancel() * * This routine simply inlines the code for the pthread_testcancel() API. * * Note: the parameter is assumed to be valid. * * RETURNS: N/A * * \NOMANUAL */ _WRS_INLINE void _pthread_testcancel_inline ( pthreadCB * pThread /* this call applies to the calling pthread */ ) { if (pThread->cancelstate == PTHREAD_CANCEL_ENABLE && pThread->cancelrequest == 1) { /* There is a pending cancellation request. Exit now */ _pthread_oncancel_exit (pThread); } } /******************************************************************************* * * _pthread_setcanceltype_inline - support routine for pthread_setcanceltype() * * This routine simply inlines the code for the pthread_setcanceltype() API. * It assumes that the type has been verified by the caller. * * Note: the parameter is assumed to be valid. * * RETURNS: 0 always. * * SEE ALSO: pthread_setcanceltype(), */ _WRS_INLINE int _pthread_setcanceltype_inline ( int type, /* new type */ int * oldtype, /* old type (out) */ pthreadCB * pThread /* this call applies to the calling pthread */ ) { /* * If the oldtype is required, save it now. Note that no validation and * locking of the pthread is necessary since 1) only the calling pthread * executes this code and, 2) the unprotected setting of the * field will not adversely affect a pthread calling pthread_cancel() at * the same time. */ if (oldtype != NULL) *oldtype = pThread->canceltype; pThread->canceltype = type; return (0); } /******************************************************************************** * pthread_testandset_canceltype - test and set a POSIX thread cancellation type * * This routine acts like pthread_setcanceltype() excepts that it will first test * whether there is a pending cancellation request and honor it accordingly. It * is used by pthread routines which act as cancellation points. Previous * implementations used only pthread_setcanceltype() and thus failed to honor * pending cancellation requests. * * The test is done with pthread_testcancel() which will honor any pending * cancellation requests and exit accordingly. * * NOTE : the pthread_setcanceltype() API cannot be modified to include the test * for pending cancellation requests as this would make it a cancellation point, * something that POSIX forbids. This is why we use a separate API. This API is * not local as it is called outside of the pthread library (by aioPxLib for * instance). Also, for performance reasons, pthread_testandset_canceltype() * actually uses inline versions of pthread_testcancel() and * pthread_setcanceltype() * * RETURNS: N/A * * \NOMANUAL */ int pthread_testandset_canceltype ( int type, /* new cancellation type */ int * oldtype /* old cancellation type (out) */ ) { pthreadCB * pThread = SELF_PTHREAD_ID; /* * The calling task may not be a POSIX thread: it can be a regular VxWorks * task using the pthreadLib API to start threads. In this case we simply * return. */ if (pThread == NULL) return 0; /* * XXX PAD - this is an internal routine. The type verification may be * overkill.. */ if (type != PTHREAD_CANCEL_ASYNCHRONOUS && type != PTHREAD_CANCEL_DEFERRED) { return EINVAL; } /* * _pthread_testcancel_inline() will test for and honor any pending * cancellation requests. If there are any, the thread will be canceled * accordingly. (SPR# 117697) */ _pthread_testcancel_inline (pThread); /* * If there is no pending request, we simply call the inline version of * pthread_setcanceltype() to do the job. */ return _pthread_setcanceltype_inline (type, oldtype, pThread); } /******************************************************************************* * * _pthread_id_self - returns the caller's pthread ID * * The _pthread_id_self() inline is a fast superset of the POSIX * pthread_self() API. It returns the calling thread's ID (a pointer to the * POSIX thread control block) and, if necessary, gives the caller a POSIX * thread persona. * * For performance, the caller's thread ID is first retrieved in the TCB. * If that value is null then the caller is a VxWorks native task which needs * being turned into a POSIX thread. * * This inline also works around compiler warnings caused by the option * -Wbad-function-cast: we need to use a temporary variable to hold the value * returned by pthread_self() and then cast it to a pthread CB pointer. * * RETURNS: a pointer to the POSIX thread's control block * * \NOMANUAL */ _WRS_INLINE pthreadCB * _pthread_id_self (void) { pthreadCB * pThread = SELF_PTHREAD_ID; if (pThread == NULL) { pthread_t threadId; threadId = pthread_self(); pThread = (pthreadCB *)threadId; } return pThread; } /******************************************************************************* * * pthread_key_allocate_data - allocate key data * * Internal function to allocate the key storage internally for the thread. * * RETURNS: pointer to the new key data * * \NOMANUAL */ static const void ** pthread_key_allocate_data (void) { const void ** new_data; if ((new_data = (const void**)malloc(sizeof(void *) * _POSIX_THREAD_KEYS_MAX))) { memset((void *)new_data, 0, sizeof(void *) * _POSIX_THREAD_KEYS_MAX); } return(new_data); } /******************************************************************************* * * cleanupPrivateData - release a thread's private data * * This is called during the cleanup of a thread (i.e. pthread_exit). It will * check to see if BOTH the destructor is a non-NULL value and that the data * value is a non-NULL value. If both of these are non-NULL values, the * destructor will be called. * * The privateDataCount is used so we do not need to go through all the keys to * determine if the destructor should be called. * * This routine may NOT manipulate the TCB of the executing task and it may * NOT call exit(). See the manual of pthread_abnormal_exit() for the * rationales. * * RETURNS: * * \NOMANUAL */ LOCAL void cleanupPrivateData ( pthreadCB * pThread ) { void * data; int key; int itr; (void)semTake (key_mutex, WAIT_FOREVER); /* can't return error */ for (itr = 0; itr < _POSIX_THREAD_DESTRUCTOR_ITERATIONS; itr++) { for (key = 0; key < _POSIX_THREAD_KEYS_MAX; key++) { if (pThread->privateDataCount) { if (pThread->privateData[key]) { data = (void *)pThread->privateData[key]; pThread->privateData[key] = NULL; pThread->privateDataCount--; if (key_table[key].destructor) { /* * Both the data and destructor are non-NULL values * so call the destructor. */ (void)semGive (key_mutex); key_table[key].destructor(data); (void)semTake (key_mutex, WAIT_FOREVER); } } } else { if (pThread->privateData != NULL) { free (pThread->privateData); pThread->privateData = NULL; } (void)semGive (key_mutex); return; } } } free (pThread->privateData); (void)semGive (key_mutex); } /******************************************************************************* * * _pthread_onexit_cleanup - do common cleanup operations on exit * * This routine does most of the cleanup and resource freeing operations common * between pthread_exit() and pthread_abnormal_exit(). * * It may NOT manipulate the TCB of the task underlying the exiting pthread and * it may NOT call taskExit(). See the manual of pthread_abnormal_exit() for * the rationales. * * RETURNS: N/A * * \NOMANUAL */ _WRS_INLINE void _pthread_onexit_cleanup ( pthreadCB * pThread, /* exiting pthread's control block */ BOOL abnormalTermination /* TRUE if abnormal termination */ ) { cleanupHandler * tmp; pthread_cond_t * cvcond = pThread->cvcond; /* * Let's indicate now the underlying VxWorks task is considered exited. * Even though the pthread control block is still around we want other * pthreads to get an error from the pthread validation. */ pThread->flags |= TASK_EXITED; /* * If we were canceled during the initialization routine of pthread_once(), * we need to do some cleanup on the control variable to leave it "as if * pthread_once() was never called". The cleanup consists of restoring the * control variable to a pristine state as well as unblocking all threads * waiting on the semaphore and then destroying it. * * This needs to be protected because other threads might just be running * pthread_once() at the same time and the PTHREAD_VALIDATE_AND_LOCK will * protect this process. */ if (pThread->pOnceControl != NULL) { /* * See comments in pthread_once() for an explanation of the logic * below. */ (void)semTake (pthreadLibOnceSemId, WAIT_FOREVER); if (pThread->pOnceControl->onceDone) { (void)semFlush (pThread->pOnceControl->onceMutex); (void) semDelete (pThread->pOnceControl->onceMutex); pThread->pOnceControl->onceMutex = NULL; } else { pThread->pOnceControl->onceCanceled = TRUE; pThread->pOnceControl->onceMyTid = 0; pThread->pOnceControl->onceInitialized = FALSE; pThread->pOnceControl->onceDone = FALSE; (void)semFlush (pThread->pOnceControl->onceMutex); (void) semDelete (pThread->pOnceControl->onceMutex); pThread->pOnceControl->onceMutex = NULL; } (void)semGive (pthreadLibOnceSemId); } /* * Call the cancellation cleanup handlers. * * In the situation of an abnormally terminated thread, some of the * operations done by the cleanup handlers may be unable to complete * properly since the code is executed in the context of the task that * terminated the POSIX thread. */ while (pThread->handlerBase) { tmp = pThread->handlerBase; pThread->handlerBase->routine (pThread->handlerBase->arg); pThread->handlerBase = pThread->handlerBase->next; free (tmp); } /* * The POSIX specs for pthread_cond_wait() and pthread_cond_timedwait() * require that a thread cancelled while blocked on a cancellation * variable re-acquire the guard mutex prior to executing the * cancellation cleanup handlers. However once the cleanup handlers have * been executed it is possible for the guard mutex to stay unavailable * for ever if none of the cleanup handlers releases it. In order to * prevent this we check whether the thread is existing while still * holding the mutex and we forcefully release this mutex if this is the * case. * * In the situation of an abnormally terminated thread, the guard mutex will * always be released (otherwise the task is not deleted), so the task * that terminated the thread re-acquired that guard mutex. */ if (cvcond && cvcond->condMutex) { cvcond->condMutex->mutexCondRefCount--; (void)pthread_mutex_unlock (cvcond->condMutex); } cleanupPrivateData (pThread); } /******************************************************************************* * * pthread_abnormal_exit - handles pthread's abnormal exit * * This function is invoked from a task delete hook to clean up after a thread * that has been deleted by a call to taskDelete() or for the case of native * VxWorks tasks turned pthreads, has exited without calling pthread_exit(). * * It does essentially the same a pthread_exit() so the two routines should be * maintained in sync, However it cannot manipulate the TCB of the executing * task, including using SELF_PTHREAD_ID, since the delete hook can be executed * in the context of the task that killed the task underlying the pthread. * * This routine assumes its parameter to point to a valid area of * memory. * * \NOMANUAL */ LOCAL void pthread_abnormal_exit ( pthreadCB * pThread ) { WIND_TCB * pTcb; pthread_cond_t * cvcond = NULL; /* * Change the exited thread's cancelability type and state to prevent * any possibility for cancellation. Although this is a moot operation for * a terminated pthread, this still ensures that the cleanup handlers * can be safe from triggering a cancellation in case they happen to be * or call cancellation points. */ pThread->canceltype = PTHREAD_CANCEL_DEFERRED; pThread->cancelstate = PTHREAD_CANCEL_DISABLE; /* * Let's not go away just yet if somebody is attempting to do something * on the abnormally terminated pthread. We have to acquire the * library's mutex prior to locking the thread in case somebody else is * attempting to validate and lock the abnormally terminated pthread * Note also that there is no way to handle semTake() or semGive() failure * here... */ (void)semTake (pthreadLibSemId, WAIT_FOREVER); /* * If the pthread validation fails here, we can not call exit() but * instead we just return. This is because pthread_abnormal_exit() is * registered as a task delete hook and if we exit, we would be exiting * out of the task that triggered the hook and would then destroy itself. * We acknowledge that returning prematurely will cause a memory leak but, * being unable to validate the thread, there is no way to know whether * freeing resources might trigger exceptions or other problems. * * IMPORTANT NOTE: we cannot use PTHREAD_VALIDATE_AND_LOCK here as this * would cause a segmentation fault for native VxWorks task. */ if (!VALID_PTHREAD (pThread, pTcb)) { (void) semGive (pthreadLibSemId); return; } (void)semExchange (pthreadLibSemId, pThread->lock, WAIT_FOREVER); /* * In case the thread got abnormally terminated while pending on a condition * variable, the deleted task re-acquires the guard mutex. This is * mimicking the cancellation scenario but we don't have to check whether * the mutex might be already acquired by the terminated thread here since * we know it cannot. * * Note: this has to be done before the call to _pthread_onexit_cleanup(). */ cvcond = pThread->cvcond; if (cvcond && cvcond->condMutex) { (void)pthread_mutex_lock (cvcond->condMutex); /* * Decrement this condition variable's reference count so that * it can be deleted. */ --cvcond->condRefCount; } /* * Execute the bulk of the exit sequence: cleanup operations, resource * freeing operations. * Note that the execution of the cleanup handlers may turn the executing * task into a POSIX thread (depending on the pthread API being used). */ /* coverity[sleep] */ _pthread_onexit_cleanup (pThread, TRUE); /* * Now handle the case of a possible joined thread and perform additional * resource freeing as necessary. */ #if DETACHED_PTHREADS_MAY_BE_JOINED if (!(pThread->flags & JOINABLE) || ((pThread->flags & DETACHED))) #else if (!(pThread->flags & JOINABLE)) #endif { /* * Note: the exiting thread is not unlocked since the mutex is going * to be deleted by pthreadCtrlBlockFree(). The resulting error coming * from pthreadValidateAndLock() will tell any thread pended for an * operation on this exiting thread that this operation cannot * proceed. */ /* coverity[sleep] */ pthreadCtrlBlockFree(pThread); return; } /* * We do not have an exit status to provide, so we use PTHREAD_CANCELED * since that's really the only status that a conforming application * might be looking for. * XXX PAD - should we introduce something like PTHREAD_ABORT? */ pThread->exitStatus = PTHREAD_CANCELED; /* * Release joiner thread if any. Note that the pthread may be unlocked * only _after_ the JOINER_WAITING flag has been checked otherwise it * would be possible for the joiner thread to free the exiting thread's * control block _before_ the exiting thread has had the time to verify * the flags. Also it is not possible to unlock the thread after the * joiner thread has been released since the exiting thread's control * block might have already been freed by the joiner thread when the * exiting thread comes to unlocking itself... */ if (pThread->flags & JOINER_WAITING) { PTHREAD_UNLOCK(pThread); (void) semGive (pThread->exitJoinSemId); } else { /* * In a normal situation the completion of the resource freeing is done * by the joiner pthread. Since this is an abnormal exit situation and * no joiner thread has been detected, we free the pthread's resources * without unlocking the pthread's control block so that a possible * late joiner get an error from PTHREAD_LOCK. */ /* coverity[sleep] */ pthreadCtrlBlockFree (pThread); } } /******************************************************************************* * * pthreadCtrlBlockFree - free the exiting pthread's control block * * This routine releases all resources associated with the exiting thread's * control block. When it returns the thread is considered gone from the * system. * * This routine may NOT manipulate the TCB of the executing task and it may * NOT call exit(). See the manual of pthread_abnormal_exit() for the * rationales. * * RETURNS: * * \NOMANUAL */ LOCAL void pthreadCtrlBlockFree ( pthreadCB *pThread ) { /* * We have to acquire the library's mutex prior to freeing the memory * used for the exiting/exited pthread's control block in order to * guarantee that anyone having acquired the library's mutex and * validated the pthread can rely on the pthread's control block to be * still valid when they attempt to lock it. */ (void)semTake (pthreadLibSemId, WAIT_FOREVER); pThread->vxTaskId = 0; pThread->flags = 0; /* mark the pthread as invalid */ /* Sanity verification to make sure private data have been freed */ if (pThread->privateData) { cleanupPrivateData(pThread); } /* * Always delete the join synchronization semaphore if it has been * created (defect 130517). */ if (pThread->exitJoinSemId != NULL) (void) semDelete(pThread->exitJoinSemId); (void) semDelete(pThread->lock); free (pThread); /* * Release the library's mutex only after the full resource freeing * operation is completed. */ (void) semGive (pthreadLibSemId); /* At this point the POSIX thread no longer exists in the system */ } /******************************************************************************* * * self_become_pthread - convert a native task into a POSIX thread * * This routine makes possible for native VxWorks tasks (i.e. tasks that are * not created via a call to pthread_create()) to use POSIX thread API. This * requires giving the native VxWorks task the minimum set of POSIX thread * attributes required to have the POSIX thread API function correctly. * * Unlike with pthreads created via pthread_create(), nobody will usually * join a native VxWorks task turned POSIX thread. It is therefore more * appropriate to not set the attachment state of the new thread to * "joinable" so that resource reclamation occurs naturally when the * newly formed thread calls pthread_exit(). * * Note that if the newly formed thread does not call pthread_exit() the * resource reclamation will be handle via the library's task delete hook * routine. * * RETURNS: N/A * * \NOMANUAL */ LOCAL void self_become_pthread (void) { pthreadCB * pThread = NULL; WIND_TCB * pTcb = (WIND_TCB *)taskIdSelf(); int vxTaskPrio; /* If the caller is already a POSIX thread, then there is nothing to do */ if ((pThread = (pthreadCB *)pTcb->pPthread) != NULL) return; /* Allocation of the pthread control block for the native task */ if (!(pThread = malloc(sizeof (pthreadCB)))) return; bzero((char *)pThread, sizeof (pthreadCB)); pThread->privateData = NULL; pThread->handlerBase = NULL; pThread->vxTaskId = (TASK_ID)pTcb; pThread->joinedPthread = NULL; /* * Create the mutex used to lock the pthread's control block as a "delete * safe" mutex. This will address the abnormal termination scenario by * preventing a pthread to go away while holding the lock of another * pthread's CB. */ pThread->lock = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE); if (pThread->lock == SEM_ID_NULL) { free (pThread); return; } pThread->lockedPthread = 0; /* * Get the priority from the associated task. The thread internal priority * contains the POSIX version so we need to convert it. */ if (taskPriorityGet (PTHREAD_TO_NATIVE_TASK (pThread), &vxTaskPrio) != OK) { free (pThread); return; } pThread->priority = PX_VX_PRI_CONV (posixPriorityNumbering, vxTaskPrio); pThread->cancelstate = PTHREAD_CANCEL_ENABLE; pThread->canceltype = PTHREAD_CANCEL_DEFERRED; pThread->cancelrequest = 0; pThread->flags = VALID; /* Register the cancellation signal handler */ signal (SIGCANCEL, sigCancelHandler); /* * Record the thread ID. At this point the native task has been turned * into a POSIX thread. */ pTcb->pPthread = pThread; } /******************************************************************************* * * pthreadValidateAndLock - validate and lock a pthread * * This routine makes possible for the caller to validate a pthread and lock it * without risking inconsistencies if ever the caller gets preempted in the * middle of this operation. * * By convention this routine is to be called by all pthreadLib functions that * need to have exclusive access to the targeted pthread's control block. * * All the mutexes involves here are inversion safe so as to minimize the * impact on concurrent pthreads. * * RETURNS: ERROR if acquiring semaphore fails or if thread is invalid, otherwise * OK * * \NOMANUAL */ LOCAL STATUS pthreadValidateAndLock ( pthreadCB * pThread, /* pthread to validate and lock */ pthreadCB * selfPthreadId /* pointer to caller's control block */ ) { WIND_TCB * pTcb; STATUS status = OK; int prevCnclType; /* * Switch to deferred cancelability type as we do not want to be * canceled after having acquired either of the library's mutex or the * pthread's control block's mutex. */ _pthread_setcanceltype_inline (PTHREAD_CANCEL_DEFERRED, &prevCnclType, selfPthreadId); /* Acquire the library's mutex prior to doing the validation */ if (semTake (pthreadLibSemId, WAIT_FOREVER) == ERROR) { pthread_testandset_canceltype (prevCnclType, NULL); return (ERROR); } /* * Prior to locking the targeted thread its ID is registered in the * caller's control block in case the caller gets cancelled while holding * the lock to the targeted thread (this would result in a situation where * the targeted thread would no longer be able to function properly or exit * cleanly). */ selfPthreadId->lockedPthread = pThread; if (VALID_PTHREAD (pThread, pTcb) == FALSE) { (void) semGive (pthreadLibSemId); selfPthreadId->lockedPthread = 0; pthread_testandset_canceltype (prevCnclType, NULL); return (ERROR); } /* * Lock the pthread. * * Use semExchange() to make sure the library's mutex is released once the * pthread's lock is acquired or pended on. * Note that the pthread lock might fail if the pthread is deleted. The * caller should be allowed to continue but should be made aware of the * problem. */ if (semExchange (pthreadLibSemId, pThread->lock, WAIT_FOREVER) == ERROR) { selfPthreadId->lockedPthread = 0; status = ERROR; } /* * Restore original cancelability type and check whether there may be * pending cancellation that have to be honoured. If the caller has been * canceled this routine does not return and the lock on the targeted * pthread is be released by _pthread_oncancel_exit(). */ pthread_testandset_canceltype (prevCnclType, NULL); return status; } /******************************************************************************* * * deadlock - simulate a deadlocked mutex * * Unlike mutex semaphores, POSIX mutexes deadlock if the owner tries to acquire * a mutex more than once. This routine ensures that the calling thread will * come to a grinding halt. This routine will only get called if there is an * application programming error. * * \NOMANUAL */ LOCAL void deadlock (void) { taskSuspend(0); }