/*
* 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 <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include "util/eventCount.h"
#else
#include <pthread.h>
#include <semaphore.h>
#include <stdbool.h>
#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 <code>true</code>, 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 */