/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/uds-releases/jasper/src/uds/threads.h#4 $ */ #ifndef THREADS_H #define THREADS_H #include "compiler.h" #include "threadOnce.h" #include "timeUtils.h" #include "uds-error.h" #ifdef __KERNEL__ #include #include #include #include "util/eventCount.h" #else #include #include #include #endif #ifdef __KERNEL__ typedef struct { EventCount *eventCount; } CondVar; typedef struct mutex Mutex; typedef struct semaphore Semaphore; typedef struct kernelThread *Thread; typedef pid_t ThreadId; typedef struct { Semaphore mutex; // Mutex for this barrier object Semaphore wait; // Semaphore for threads waiting at the barrier int arrived; // Number of threads which have arrived int threadCount; // Total number of threads using this barrier } Barrier; #else typedef pthread_barrier_t Barrier; typedef pthread_cond_t CondVar; typedef pthread_mutex_t Mutex; typedef sem_t Semaphore; typedef pthread_t Thread; typedef pid_t ThreadId; #ifndef NDEBUG #define MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP #else #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #endif extern const bool DO_ASSERTIONS; #endif #ifdef __KERNEL__ /** * Apply a function to every thread that we have created. * * @param applyFunc The function to apply * @param argument The first argument to applyFunc * **/ void applyToThreads(void applyFunc(void *, struct task_struct *), void *argument); #endif /** * Create a thread, logging any cause of failure. * * @param threadFunc function to run in new thread * @param threadData private data for new thread * @param name name of the new thread * @param newThread where to store the new thread id * * @return success or failure indication **/ int createThread(void (*threadFunc)(void *), void *threadData, const char *name, Thread *newThread) __attribute__((warn_unused_result)); /** * Retrieve the current numbers of cores. * * This is either the total number or the number of cores that this * process has been limited to. * * @return number of cores **/ unsigned int getNumCores(void); /** * Return the id of the current thread. * * @return the thread id **/ ThreadId getThreadId(void) __attribute__((warn_unused_result)); #ifndef __KERNEL__ /** * Get the name of the current thread. * * @param name a buffer of size at least 16 to write the name to **/ void getThreadName(char *name); #endif /** * Wait for termination of another thread. * * * @param th The thread for which to wait. * * @return UDS_SUCCESS or error code **/ int joinThreads(Thread th); #ifdef __KERNEL__ /** * Exit the current thread. This is a kernel-only function that is intended to * be an alternative to using BUG() or BUG_ON(). **/ __attribute__((noreturn)) void exitThread(void); #endif /** * Initialize a thread synchronization barrier (also known as a rendezvous). * * @param barrier the barrier to initialize * @param threadCount the number of threads that must enter the barrier before * any threads are permitted to leave it * * @return UDS_SUCCESS or an error code **/ int initializeBarrier(Barrier *barrier, unsigned int threadCount) __attribute__((warn_unused_result)); /** * Destroy a thread synchronization barrier. * * @param barrier the barrier to destroy * * @return UDS_SUCCESS or an error code **/ int destroyBarrier(Barrier *barrier); /** * Enter a thread synchronization barrier, waiting for the configured number * of threads to have entered before exiting the barrier. Exactly one thread * will be arbitrarily selected to be flagged as the "winner" of a barrier. * * @param barrier the barrier to enter * @param winner if non-NULL, a pointer to the flag indicating whether the * calling thread was the unique winner * * @return UDS_SUCCESS or an error code **/ int enterBarrier(Barrier *barrier, bool *winner); /** * Initialize a condition variable with default attributes. * * @param cond condition variable to init * * @return UDS_SUCCESS or error code **/ int initCond(CondVar *cond) __attribute__((warn_unused_result)); /** * Signal a condition variable. * * @param cond condition variable to signal * * @return UDS_SUCCESS or error code **/ int signalCond(CondVar *cond); /** * Broadcast a condition variable. * * @param cond condition variable to broadcast * * @return UDS_SUCCESS or error code **/ int broadcastCond(CondVar *cond); /** * Wait on a condition variable. * * @param cond condition variable to wait on * @param mutex mutex to release while waiting * * @return UDS_SUCCESS or error code **/ int waitCond(CondVar *cond, Mutex *mutex); /** * Wait on a condition variable with a timeout. * * @param cond condition variable to wait on * @param mutex mutex to release while waiting * @param timeout the relative time until the timeout expires * * @return error code (ETIMEDOUT if the deadline is hit) **/ int timedWaitCond(CondVar *cond, Mutex *mutex, RelTime timeout); /** * Destroy a condition variable. * * @param cond condition variable to destroy * * @return UDS_SUCCESS or error code **/ int destroyCond(CondVar *cond); #ifndef __KERNEL__ /** * Initialize a mutex, optionally asserting if the mutex initialization fails. * This function should only be called directly in places where making * assertions is not safe. * * @param mutex the mutex to initialize * @param assertOnError if true, an error initializing the * mutex will make an assertion * * @return UDS_SUCCESS or an error code **/ int initializeMutex(Mutex *mutex, bool assertOnError); #endif /** * Initialize the default type (error-checking during development) mutex. * * @param mutex the mutex to initialize * * @return UDS_SUCCESS or an error code **/ __attribute__((warn_unused_result)) #ifdef __KERNEL__ static INLINE int initMutex(Mutex *mutex) { mutex_init(mutex); return UDS_SUCCESS; } #else int initMutex(Mutex *mutex); #endif /** * Destroy a mutex (with error checking during development). * * @param mutex mutex to destroy * * @return UDS_SUCCESS or error code **/ #ifdef __KERNEL__ static INLINE int destroyMutex(Mutex *mutex) { return UDS_SUCCESS; } #else int destroyMutex(Mutex *mutex); #endif /** * Lock a mutex, with optional error checking during development. * * @param mutex mutex to lock **/ #ifdef __KERNEL__ static INLINE void lockMutex(Mutex *mutex) { mutex_lock(mutex); } #else void lockMutex(Mutex *mutex); #endif /** * Unlock a mutex, with optional error checking during development. * * @param mutex mutex to unlock **/ #ifdef __KERNEL__ static INLINE void unlockMutex(Mutex *mutex) { mutex_unlock(mutex); } #else void unlockMutex(Mutex *mutex); #endif /** * Initialize a semaphore used among threads in the same process. * * @param semaphore the semaphore to initialize * @param value the initial value of the semaphore * * @return UDS_SUCCESS or an error code **/ __attribute__((warn_unused_result)) #ifdef __KERNEL__ static INLINE int initializeSemaphore(Semaphore *semaphore, unsigned int value) { sema_init(semaphore, value); return UDS_SUCCESS; } #else int initializeSemaphore(Semaphore *semaphore, unsigned int value); #endif /** * Destroy a semaphore used among threads in the same process. * * @param semaphore the semaphore to destroy * * @return UDS_SUCCESS or an error code **/ #ifdef __KERNEL__ static INLINE int destroySemaphore(Semaphore *semaphore) { return UDS_SUCCESS; } #else int destroySemaphore(Semaphore *semaphore); #endif /** * Acquire a permit from a semaphore, waiting if none are currently available. * * @param semaphore the semaphore to acquire **/ #ifdef __KERNEL__ static INLINE void acquireSemaphore(Semaphore *semaphore) { // Do not use down(semaphore). Instead use down_interruptible so that we do // not get 120 second stall messages in kern.log. while (down_interruptible(semaphore) != 0) { } } #else void acquireSemaphore(Semaphore *semaphore); #endif /** * Attempt to acquire a permit from a semaphore. * * If a permit is available, it is claimed and the function immediately * returns true. If a timeout is zero or negative, the function immediately * returns false. Otherwise, this will wait either a permit to become * available (returning true) or the relative timeout to expire (returning * false). * * @param semaphore the semaphore to decrement * @param timeout the relative time until the timeout expires * * @return true if a permit was acquired, otherwise false **/ __attribute__((warn_unused_result)) #ifdef __KERNEL__ static INLINE bool attemptSemaphore(Semaphore *semaphore, RelTime timeout) { if (timeout <= 0) { // No timeout, just try to grab the semaphore. return down_trylock(semaphore) == 0; } else { unsigned int jiffies = usecs_to_jiffies(relTimeToMicroseconds(timeout)); return down_timeout(semaphore, jiffies) == 0; } } #else bool attemptSemaphore(Semaphore *semaphore, RelTime timeout); #endif /** * Release a semaphore, incrementing the number of available permits. * * @param semaphore the semaphore to increment **/ #ifdef __KERNEL__ static INLINE void releaseSemaphore(Semaphore *semaphore) { up(semaphore); } #else void releaseSemaphore(Semaphore *semaphore); #endif /** * Yield the time slice in the given thread. * * @return UDS_SUCCESS or an error code **/ int yieldScheduler(void); #ifndef __KERNEL__ /** * Allocate a thread specific key for thread specific data. * * @param key points to location for new key * @param destr_function destructor function called when thread exits * * @return UDS_SUCCESS or error code **/ int createThreadKey(pthread_key_t *key, void (*destr_function) (void *)); /** * Delete a thread specific key for thread specific data. * * @param key key to delete * * @return UDS_SUCCESS or error code **/ int deleteThreadKey(pthread_key_t key); /** * Set pointer for thread specific data. * * @param key key to be associated with pointer * @param pointer data associated with key * * @return UDS_SUCCESS or error code **/ int setThreadSpecific(pthread_key_t key, const void *pointer); /** * Get pointer for thread specific data. * * @param key key identifying the thread specific data **/ void *getThreadSpecific(pthread_key_t key); #endif #endif /* THREADS_H */