/* _pthreadw_wrappers.h
 *
 * Copyright (C) 2007 Christian Bienia
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * USA.
 */

/* This file implements the user-level wrappers of the libpthread functions.
 * Do not include this header directly in your program, use the provided
 * substitutes pthread.h and semaphore.h instead which will setup everything
 * correctly (hopefully).
 *
 * This header is used in multiple other header files, all of which can in
 * turn be included in the same source code file. It is therefore includable
 * multiple times. Header files which use this header file must #define the
 * correct macros to get the desired functionality. The header recognizes the
 * following macros, at least one of which has to be #defined to get anything:
 *
 * - __PTHREADW__WITH_PTHREAD_MUTEXES
 * - __PTHREADW__WITH_PTHREAD_CONDITIONS
 * - __PTHREADW__WITH_PTHREAD_BARRIERS
 * - __PTHREADW__WITH_PTHREAD_RWLOCKS
 * - __PTHREADW__WITH_PTHREAD_SPINS
 * - __PTHREADW__WITH_SEMAPHORES
 *
 * For additional functionality furthermore:
 * - __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS
 */

/* Get dlsym and RTLD_DEFAULT */
#include <dlfcn.h>
/* Include correct header files */
/* Note: Real pthread.h always required for pthread_once */
#include "/usr/include/pthread.h"
#include "/usr/include/semaphore.h"


/* Make function name available in a compiler-independent way */
#if __STDC_VERSION__ < 199901L
# if __GNUC__ >= 2
#  define __PTHREADW_FUNC__ __FUNCTION__
# else
#  define __PTHREADW_FUNC__ NULL
# endif
#else
# define __PTHREADW_FUNC__ __func__
#endif

/* Declare handler functions and variables only once */
#ifndef __PTHREADW__PTR_HANDLER_DECLARED__
#define __PTHREADW__PTR_HANDLER_DECLARED__ 1
/* Function pointer used to call _threadw_ptr_alias
 * Implemented as union because we need an ISO C99 compatible
 * way to cast an object pointer to a function pointer.
 */
static union {
  void *optr;
  void (*fptr)(void *, const char *, const char *, const char *, int);
} __PTHREADW__ptr_union;

/* Initialization routine for handler */
static void __PTHREADW__init_routine() {
  __PTHREADW__ptr_union.optr = dlsym(RTLD_DEFAULT, "_threadw_ptr_alias");
}
static pthread_once_t __PTHREADW__is_initialized = PTHREAD_ONCE_INIT;

/* Handler which is used to pass information pertaining a synchronization
 * variable pointer to pthreadw profiling library.
 */
static inline void __PTHREADW__handle_ptr_alias(void *vptr, const char *name, const char *file, const char *func, int line) {
  /* Initialize function pointer */
  pthread_once(&__PTHREADW__is_initialized, &__PTHREADW__init_routine);

  /* If pthreadw could be found pass on pointer information */
  if(__PTHREADW__ptr_union.optr!=NULL) {
    (*(__PTHREADW__ptr_union.fptr))(vptr, name, file, func, line);
  }
}
#endif /* __PTHREADW__PTR_HANDLER_DECLARED__ */



/* Wrappers for mutex functions */

#if defined(__PTHREADW__WITH_PTHREAD_MUTEXES)
static inline int __PTHREADW__pthread_mutex_init(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_init(mutex, attr);
}
#undef pthread_mutex_init
#define pthread_mutex_init(m, a) __PTHREADW__##pthread_mutex_init(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m, a)

static inline int __PTHREADW__pthread_mutex_lock(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_lock(mutex);
}
#undef pthread_mutex_lock
#define pthread_mutex_lock(m) __PTHREADW__##pthread_mutex_lock(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m)

static inline int __PTHREADW__pthread_mutex_trylock(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_trylock(mutex);
}
#undef pthread_mutex_trylock
#define pthread_mutex_trylock(m) __PTHREADW__##pthread_mutex_trylock(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m)

static inline int __PTHREADW__pthread_mutex_timedlock(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex, const struct timespec *abstime) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_timedlock(mutex, abstime);
}
#undef pthread_mutex_timedlock
#define pthread_mutex_timedlock(m, t) __PTHREADW__##pthread_mutex_trylock(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m, t)

#if defined(__PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS)
static inline int __PTHREADW__pthread_mutex_reltimedlock_np(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex, const struct timespec *reltime) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_reltimedlock_np(mutex, reltime);
}
#undef pthread_mutex_reltimedlock_np
#define pthread_mutex_reltimedlock_np(m, t) __PTHREADW__##pthread_mutex_reltimedlock_np(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m, t)
#endif /* __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS */

static inline int __PTHREADW__pthread_mutex_unlock(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_unlock(mutex);
}
#undef pthread_mutex_unlock
#define pthread_mutex_unlock(m) __PTHREADW__##pthread_mutex_unlock(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m)

static inline int __PTHREADW__pthread_mutex_destroy(const char *name, const char *file, const char *func, int line, pthread_mutex_t *mutex) {
  __PTHREADW__handle_ptr_alias((void *)mutex, name, file, func, line);
  return pthread_mutex_destroy(mutex);
}
#undef pthread_mutex_destroy
#define pthread_mutex_destroy(m) __PTHREADW__##pthread_mutex_destroy(#m, __FILE__, __PTHREADW_FUNC__, __LINE__, m)
#endif /* __PTHREADW__WITH_PTHREAD_MUTEXES */

/* Wrappers for condition functions */

#if defined(__PTHREADW__WITH_PTHREAD_CONDITIONS)
static inline int __PTHREADW__pthread_cond_init(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond, const pthread_condattr_t *attr) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_init(cond, attr);
}
#undef pthread_cond_init
#define pthread_cond_init(c, a) __PTHREADW__##pthread_cond_init(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c, a)

static inline int __PTHREADW__pthread_cond_wait(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond, pthread_mutex_t *mutex) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_wait(cond, mutex);
}
#undef pthread_cond_wait
#define pthread_cond_wait(c, m) __PTHREADW__##pthread_cond_wait(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c, m)

static inline int __PTHREADW__pthread_cond_timedwait(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_timedwait(cond, mutex, abstime);
}
#undef pthread_cond_timedwait
#define pthread_cond_timedwait(c, m, t) __PTHREADW__##pthread_cond_timedwait(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c, m, t)

static inline int __PTHREADW__pthread_cond_signal(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_signal(cond);
}
#undef pthread_cond_signal
#define pthread_cond_signal(c) __PTHREADW__##pthread_cond_signal(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c)

static inline int __PTHREADW__pthread_cond_broadcast(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_broadcast(cond);
}
#undef pthread_cond_broadcast
#define pthread_cond_broadcast(c) __PTHREADW__##pthread_cond_broadcast(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c)

static inline int __PTHREADW__pthread_cond_destroy(const char *name, const char *file, const char *func, int line, pthread_cond_t *cond) {
  __PTHREADW__handle_ptr_alias((void *)cond, name, file, func, line);
  return pthread_cond_destroy(cond);
}
#undef pthread_cond_destroy
#define pthread_cond_destroy(c) __PTHREADW__##pthread_cond_destroy(#c, __FILE__, __PTHREADW_FUNC__, __LINE__, c)
#endif /* __PTHREADW__WITH_PTHREAD_CONDITIONS */

/* Wrappers for barrier functions */

#if defined(__PTHREADW__WITH_PTHREAD_BARRIERS)
static inline int __PTHREADW__pthread_barrier_init(const char *name, const char *file, const char *func, int line, pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned count) {
  __PTHREADW__handle_ptr_alias((void *)barrier, name, file, func, line);
  return pthread_barrier_init(barrier, attr, count);
}
#undef pthread_barrier_init
#define pthread_barrier_init(b, a, c) __PTHREADW__##pthread_barrier_init(#b, __FILE__, __PTHREADW_FUNC__, __LINE__, b, a, c)

static inline int __PTHREADW__pthread_barrier_wait(const char *name, const char *file, const char *func, int line, pthread_barrier_t *barrier) {
  __PTHREADW__handle_ptr_alias((void *)barrier, name, file, func, line);
  return pthread_barrier_wait(barrier);
}
#undef pthread_barrier_wait
#define pthread_barrier_wait(b) __PTHREADW__##pthread_barrier_wait(#b, __FILE__, __PTHREADW_FUNC__, __LINE__, b)

static inline int __PTHREADW__pthread_barrier_destroy(const char *name, const char *file, const char *func, int line, pthread_barrier_t *barrier) {
  __PTHREADW__handle_ptr_alias((void *)barrier, name, file, func, line);
  return pthread_barrier_destroy(barrier);
}
#undef pthread_barrier_destroy
#define pthread_barrier_destroy(b) __PTHREADW__##pthread_barrier_destroy(#b, __FILE__, __PTHREADW_FUNC__, __LINE__, b)
#endif /* __PTHREADW__WITH_PTHREAD_BARRIERS */

/* Wrappers for rwlock functions */

#ifdef __PTHREADW__WITH_PTHREAD_RWLOCKS
static inline int __PTHREADW__pthread_rwlock_init(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_init(rwlock, attr);
}
#undef pthread_rwlock_init
#define pthread_rwlock_init(l, a) __PTHREADW__##pthread_rwlock_init(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, a)

static inline int __PTHREADW__pthread_rwlock_rdlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_rdlock(rwlock);
}
#undef pthread_rwlock_rdlock
#define pthread_rwlock_rdlock(l) __PTHREADW__##pthread_rwlock_rdlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_rwlock_tryrdlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_tryrdlock(rwlock);
}
#undef pthread_rwlock_tryrdlock
#define pthread_rwlock_tryrdlock(l) __PTHREADW__##pthread_rwlock_tryrdlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_rwlock_timedrdlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock, const struct timespec *abstime) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_timedrdlock(rwlock, abstime);
}
#undef pthread_rwlock_timedrdlock
#define pthread_rwlock_timedrdlock(l, t) __PTHREADW__##pthread_rwlock_timedrdlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, t)

#ifdef __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS
static inline int __PTHREADW__pthread_rwlock_reltimedrdlock_np(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock, const struct timespec *reltime) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_reltimedrdlock_np(rwlock, reltime);
}
#undef pthread_rwlock_reltimedrdlock_np
#define pthread_rwlock_reltimedrdlock_np(l, t) __PTHREADW__##pthread_rwlock_reltimedrdlock_np(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, t)
#endif /* __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS */

static inline int __PTHREADW__pthread_rwlock_wrlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_wrlock(rwlock);
}
#undef pthread_rwlock_wrlock
#define pthread_rwlock_wrlock(l) __PTHREADW__##pthread_rwlock_wrlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_rwlock_trywrlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_trywrlock(rwlock);
}
#undef pthread_rwlock_trywrlock
#define pthread_rwlock_trywrlock(l) __PTHREADW__##pthread_rwlock_trywrlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_rwlock_timedwrlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock, const struct timespec *abstime) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_timedwrlock(rwlock, abstime);
}
#undef pthread_rwlock_timedwrlock
#define pthread_rwlock_timedwrlock(l, t) __PTHREADW__##pthread_rwlock_timedwrlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, t)

#ifdef __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS
static inline int __PTHREADW__pthread_rwlock_reltimedwrlock_np(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock, const struct timespec *reltime) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_reltimedwrlock_np(rwlock, reltime);
}
#undef pthread_rwlock_reltimedwrlock_np
#define pthread_rwlock_reltimedwrlock_np(l, t) __PTHREADW__##pthread_rwlock_reltimedwrlock_np(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, t)
#endif /* __PTHREADW__WITH_PTHREAD_RELTIMEDFUNCS */

static inline int __PTHREADW__pthread_rwlock_unlock(const char *name, const char *file, const char *func, int line, pthread_rwlock_t *rwlock) {
  __PTHREADW__handle_ptr_alias((void *)rwlock, name, file, func, line);
  return pthread_rwlock_unlock(rwlock);
}
#undef pthread_rwlock_unlock
#define pthread_rwlock_unlock(l) __PTHREADW__##pthread_rwlock_unlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)
#endif /* __PTHREADW__WITH_PTHREAD_RWLOCKS */

/* Wrappers for spinlock functions */

#ifdef __PTHREADW__WITH_PTHREAD_SPINS
static inline int __PTHREADW__pthread_spin_init(const char *name, const char *file, const char *func, int line, pthread_spinlock_t *spin, int pshared) {
  __PTHREADW__handle_ptr_alias((void *)spin, name, file, func, line);
  return pthread_spin_init(spin, pshared);
}
#undef pthread_spin_init
#define pthread_spin_init(l, p) __PTHREADW__##pthread_spin_init(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l, p)

static inline int __PTHREADW__pthread_spin_lock(const char *name, const char *file, const char *func, int line, pthread_spinlock_t *lock) {
  __PTHREADW__handle_ptr_alias((void *)lock, name, file, func, line);
  return pthread_spin_lock(lock);
}
#undef pthread_spin_lock
#define pthread_spin_lock(l) __PTHREADW__##pthread_spin_lock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_spin_trylock(const char *name, const char *file, const char *func, int line, pthread_spinlock_t *lock) {
  __PTHREADW__handle_ptr_alias((void *)lock, name, file, func, line);
  return pthread_spin_trylock(lock);
}
#undef pthread_spin_trylock
#define pthread_spin_trylock(l) __PTHREADW__##pthread_spin_trylock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)

static inline int __PTHREADW__pthread_spin_unlock(const char *name, const char *file, const char *func, int line, pthread_spinlock_t *lock) {
  __PTHREADW__handle_ptr_alias((void *)lock, name, file, func, line);
  return pthread_spin_unlock(lock);
}
#undef pthread_spin_unlock
#define pthread_spin_unlock(l) __PTHREADW__##pthread_spin_unlock(#l, __FILE__, __PTHREADW_FUNC__, __LINE__, l)
#endif /* __PTHREADW__WITH_PTHREAD_SPINS */

/* Wrappers for semaphore functions */

#ifdef __PTHREADW__WITH_SEMAPHORES
#endif /* __PTHREADW__WITH_SEMAPHORES */
