blob: c2463cd2aad09afe20303a2eb1d1ac7b27b093f0 [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*/
/**
* @file common_utils.h
* @brief Common tools for printing, parsing, ....
*/
#ifndef COMMON_UTILS_H
#define COMMON_UTILS_H
#include <time.h>
#include <assert.h>
#include <pthread.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "gsh_types.h"
#include "log.h"
/**
* BUILD_BUG_ON - break compile if a condition is true.
* @condition: the condition which the compiler should know is false.
*
* If you have some code which relies on certain constants being equal, or
* other compile-time-evaluated condition, you should use BUILD_BUG_ON to
* detect if someone changes it.
*
* The implementation uses gcc's reluctance to create a negative array, but
* gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments
* to inline functions). So as a fallback we use the optimizer; if it can't
* prove the condition is false, it will cause a link error on the undefined
* "__build_bug_on_failed". This error message can be harder to track down
* though, hence the two different methods.
*
* Blatantly stolen from kernel source, include/linux/kernel.h:651
*/
#ifndef __OPTIMIZE__
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
#else
extern int __build_bug_on_failed;
#define BUILD_BUG_ON(condition) \
do { \
((void)sizeof(char[1 - 2*!!(condition)])); \
if (condition) \
__build_bug_on_failed = 1; \
} while (0)
#endif
/* Most machines scandir callback requires a const. But not all */
#define SCANDIR_CONST const
/* Most machines have mntent.h. */
#define HAVE_MNTENT_H 1
/* String parsing functions */
#ifndef HAVE_STRLCPY
extern size_t strlcpy(char *dst, const char *src, size_t siz);
#endif
#ifndef HAVE_STRNLEN
#define strnlen(a, b) gsh_strnlen(a, b)
/* prefix with gsh_ to prevent library conflict -- will fix properly
with new build system */
extern size_t gsh_strnlen(const char *s, size_t max);
#endif
#if defined(__APPLE__)
#define clock_gettime(a, ts) portable_clock_gettime(ts)
extern int portable_clock_gettime(struct timespec *ts);
#define pthread_yield() pthread_yield_np()
#undef SCANDIR_CONST
#define SCANDIR_CONST
#undef HAVE_MNTENT_H
#endif
#if defined(__FreeBSD__)
#undef SCANDIR_CONST
#define SCANDIR_CONST
#endif
/**
* @brief Logging rwlock initialization
*
* @param[in,out] _lock The rwlock to initialize
* @param[in,out] _attr The attributes used while initializing the lock
*/
#define PTHREAD_RWLOCK_init(_lock, _attr) \
do { \
int rc; \
\
rc = pthread_rwlock_init(_lock, _attr); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Init rwlock %p (%s) at %s:%d", \
_lock, #_lock, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Init rwlock %p (%s) " \
"at %s:%d", rc, _lock, #_lock, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging rwlock destroy
*
* @param[in,out] _lock The rwlock to destroy
*/
#define PTHREAD_RWLOCK_destroy(_lock) \
do { \
int rc; \
\
rc = pthread_rwlock_destroy(_lock); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Destroy mutex %p (%s) at %s:%d", \
_lock, #_lock, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Destroy mutex %p (%s) " \
"at %s:%d", rc, _lock, #_lock, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging write-lock
*
* @param[in,out] _lock Read-write lock
*/
#define PTHREAD_RWLOCK_wrlock(_lock) \
do { \
int rc; \
\
rc = pthread_rwlock_wrlock(_lock); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Got write lock on %p (%s) " \
"at %s:%d", _lock, #_lock, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, write locking %p (%s) " \
"at %s:%d", rc, _lock, #_lock, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0) \
/**
* @brief Logging read-lock
*
* @param[in,out] _lock Read-write lock
*/
#define PTHREAD_RWLOCK_rdlock(_lock) \
do { \
int rc; \
\
rc = pthread_rwlock_rdlock(_lock); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Got read lock on %p (%s) " \
"at %s:%d", _lock, #_lock, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, read locking %p (%s) " \
"at %s:%d", rc, _lock, #_lock, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0) \
/**
* @brief Logging read-write lock unlock
*
* @param[in,out] _lock Read-write lock
*/
#define PTHREAD_RWLOCK_unlock(_lock) \
do { \
int rc; \
\
rc = pthread_rwlock_unlock(_lock); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Unlocked %p (%s) at %s:%d", \
_lock, #_lock, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, unlocking %p (%s) at %s:%d", \
rc, _lock, #_lock, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0) \
/**
* @brief Logging mutex lock
*
* @param[in,out] _mtx The mutex to acquire
*/
#define PTHREAD_MUTEX_lock(_mtx) \
do { \
int rc; \
\
rc = pthread_mutex_lock(_mtx); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Acquired mutex %p (%s) at %s:%d", \
_mtx, #_mtx, \
__FILE__, __LINE__); \
} else{ \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, acquiring mutex %p (%s) " \
"at %s:%d", rc, _mtx, #_mtx, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging mutex unlock
*
* @param[in,out] _mtx The mutex to relinquish
*/
#define PTHREAD_MUTEX_unlock(_mtx) \
do { \
int rc; \
\
rc = pthread_mutex_unlock(_mtx); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Released mutex %p (%s) at %s:%d", \
_mtx, #_mtx, \
__FILE__, __LINE__); \
} else{ \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, releasing mutex %p (%s) " \
"at %s:%d", rc, _mtx, #_mtx, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging mutex initialization
*
* @param[in,out] _mtx The mutex to initialize
* @param[in,out] _attr The attributes used while initializing the mutex
*/
#define PTHREAD_MUTEX_init(_mtx, _attr) \
do { \
int rc; \
\
rc = pthread_mutex_init(_mtx, _attr); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Init mutex %p (%s) at %s:%d", \
_mtx, #_mtx, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Init mutex %p (%s) " \
"at %s:%d", rc, _mtx, #_mtx, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging mutex destroy
*
* @param[in,out] _mtx The mutex to destroy
*/
#define PTHREAD_MUTEX_destroy(_mtx) \
do { \
int rc; \
\
rc = pthread_mutex_destroy(_mtx); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Destroy mutex %p (%s) at %s:%d", \
_mtx, #_mtx, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Destroy mutex %p (%s) " \
"at %s:%d", rc, _mtx, #_mtx, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging condition variable initialization
*
* @param[in,out] _cond The condition variable to initialize
* @param[in,out] _attr The attributes used while initializing the
* condition variable
*/
#define PTHREAD_COND_init(_cond, _attr) \
do { \
int rc; \
\
rc = pthread_cond_init(_cond, _attr); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Init cond %p (%s) at %s:%d", \
_cond, #_cond, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Init cond %p (%s) " \
"at %s:%d", rc, _cond, #_cond, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Logging condtion variable destroy
*
* @param[in,out] _cond The condition variable to destroy
*/
#define PTHREAD_COND_destroy(_cond) \
do { \
int rc; \
\
rc = pthread_cond_destroy(_cond); \
if (rc == 0) { \
LogFullDebug(COMPONENT_RW_LOCK, \
"Destroy cond %p (%s) at %s:%d", \
_cond, #_cond, \
__FILE__, __LINE__); \
} else { \
LogCrit(COMPONENT_RW_LOCK, \
"Error %d, Destroy cond %p (%s) " \
"at %s:%d", rc, _cond, #_cond, \
__FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* @brief Inline functions for timespec math
*
* This is for timespec math. If you want to do
* do the same kinds of math on timeval values,
* See timeradd(3) in GLIBC.
*
* The primary purpose of nsecs_elapsed_t is for a compact
* and quick way to handle time issues relative to server
* start and server EPOCH (which is not quite the same thing
* but too complicated to explain here).
*/
/**
* @brief Get the abs difference between two timespecs in nsecs
*
* useful for cheap time calculation. Works with Dr. Who...
*
* @param[in] start timespec of before end
* @param[in] end timespec after start time
*
* @return Elapsed time in nsecs
*/
static inline nsecs_elapsed_t
timespec_diff(const struct timespec *start,
const struct timespec *end)
{
if ((end->tv_sec > start->tv_sec)
|| (end->tv_sec == start->tv_sec
&& end->tv_nsec >= start->tv_nsec)) {
return (end->tv_sec - start->tv_sec) * NS_PER_SEC +
(end->tv_nsec - start->tv_nsec);
} else {
return (start->tv_sec - end->tv_sec) * NS_PER_SEC +
(start->tv_nsec - end->tv_nsec);
}
}
/**
* @brief Convert a timespec to an elapsed time interval
*
* This will work for wallclock time until 2554.
*/
static
inline nsecs_elapsed_t timespec_to_nsecs(struct timespec *timespec)
{
return timespec->tv_sec * NS_PER_SEC + timespec->tv_nsec;
}
/**
* @brief Convert an elapsed time interval to a timespec
*/
static
inline void nsecs_to_timespec(nsecs_elapsed_t interval,
struct timespec *timespec)
{
timespec->tv_sec = interval / NS_PER_SEC;
timespec->tv_nsec = interval % NS_PER_SEC;
}
/**
* @brief Add an interval to a timespec
*
* @param[in] interval Nanoseconds to add
* @param[in,out] timespec Time
*/
static inline void
timespec_add_nsecs(nsecs_elapsed_t interval,
struct timespec *timespec)
{
timespec->tv_sec += (interval / NS_PER_SEC);
timespec->tv_nsec += (interval % NS_PER_SEC);
if (timespec->tv_nsec > NS_PER_SEC) {
timespec->tv_sec += (timespec->tv_nsec / NS_PER_SEC);
timespec->tv_nsec = timespec->tv_nsec % NS_PER_SEC;
}
}
/**
* @brief Subtract an interval from a timespec
*
* @param[in] interval Nanoseconds to subtract
* @param[in,out] timespec Time
*/
static inline void
timespec_sub_nsecs(nsecs_elapsed_t interval, struct timespec *t)
{
struct timespec ts;
nsecs_to_timespec(interval, &ts);
if (ts.tv_nsec > t->tv_nsec) {
t->tv_sec -= (ts.tv_sec + 1);
t->tv_nsec = ts.tv_nsec - t->tv_nsec;
} else {
t->tv_sec -= ts.tv_sec;
t->tv_nsec -= ts.tv_nsec;
}
}
/**
* @brief Compare two times
*
* Determine if @c t1 is less-than, equal-to, or greater-than @c t2.
*
* @param[in] t1 First time
* @param[in] t2 Second time
*
* @retval -1 @c t1 is less-than @c t2
* @retval 0 @c t1 is equal-to @c t2
* @retval 1 @c t1 is greater-than @c t2
*/
static inline int gsh_time_cmp(const struct timespec *t1,
const struct timespec *t2)
{
if (t1->tv_sec < t2->tv_sec) {
return -1;
} else if (t1->tv_sec > t2->tv_sec) {
return 1;
} else {
if (t1->tv_nsec < t2->tv_nsec)
return -1;
else if (t1->tv_nsec > t2->tv_nsec)
return 1;
}
return 0;
}
/**
* @brief Get the time right now as a timespec
*
* @param[out] ts Timespec struct
*/
static inline void now(struct timespec *ts)
{
int rc;
rc = clock_gettime(CLOCK_REALTIME, ts);
if (rc != 0) {
LogCrit(COMPONENT_MAIN, "Failed to get timestamp");
assert(0); /* if this is broken, we are toast so die */
}
}
/**
* @brief Copy a string into a buffer safely
*
* This function doesn't overflow and and and makes sure the buffer is
* null terminated.
*
* @param[out] dest Destination buffer
* @param[in] src Source string
* @param[in] dest_size Total size of dest
*
* @retval 0 on success.
* @retval -1 if the buffer would overflow (the buffer is not modified)
*/
static inline int strmaxcpy(char *dest, const char *src, size_t dest_size)
{
size_t len = strlen(src);
if (len >= dest_size)
return -1;
memcpy(dest, src, len + 1);
return 0;
}
/**
* @brief Append a string to buffer safely
*
* This function doesn't overflow the buffer, and makes sure the
* buffer is null terminated.
*
* @param[in,out] dest Destination buffer
* @param[in] src Source string
* @param[in] dest_size Total size of dest
*
* @retval 0 on success.
* @retval -1 if the buffer would overflow (the buffer is not modified).
*/
static inline int strmaxcat(char *dest, const char *src, size_t dest_size)
{
int destlen = strlen(dest);
int remain = dest_size - destlen;
int srclen = strlen(src);
if (remain <= srclen)
return -1;
memcpy(dest + destlen, src, srclen + 1);
return 0;
}
#endif /* !COMMON_UTILS_H */