/*
 * nasd_threads_pthread.h
 *
 * pthread variant of threads portability layer.
 *
 * Authors: Mark Holland, Daniel Stodolsky, Jim Zelenka, Marc Unangst
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1995,1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#ifndef _NASD__NASD_THREADS_PTHREAD_H_
#define _NASD__NASD_THREADS_PTHREAD_H_

#include <nasd/nasd_options.h>

#if defined(LINUX) && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)
#include <dce/exc_handling.h>
#define LINUX__NASD_PTHREAD_EXTRAS_DEFINED
#endif /* LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE) */

#if defined(LINUX) && !defined(LINUX__NASD_PTHREAD_EXTRAS_DEFINED)
/*
 * Linux pthreads is missing some key definitions. Linux DCE stuffs them
 * in, though. So we must do this dance of love.
 */
typedef void  (*pthread_initroutine_t)(void);
typedef void * pthread_addr_t;
#define MUTEX_FAST_NP PTHREAD_MUTEX_FAST_NP
#define pthread_mutexattr_create(_attrp_)  pthread_mutexattr_init(_attrp_)
#define pthread_mutexattr_delete(_attrp_)  pthread_mutexattr_destroy(_attrp_)
#define pthread_condattr_create(_attrp_)   pthread_condattr_init(_attrp_)
#define pthread_condattr_delete(_attrp_)   pthread_condattr_destroy(_attrp_)
#define pthread_attr_create(_attrp_)       pthread_attr_init(_attrp_)
#define pthread_attr_delete(_attrp_)       pthread_attr_destroy(_attrp_)
typedef void * (*pthread_startroutine_t)(void *);
#endif /* LINUX && !LINUX__NASD_PTHREAD_EXTRAS_DEFINED */

#if defined(FREEBSD) || defined(NETBSD)
typedef void	(*pthread_initroutine_t)(void);
typedef void * (*pthread_startroutine_t)(void *);
#define pthread_mutexattr_create(_attrp_)  pthread_mutexattr_init(_attrp_)
#define pthread_mutexattr_delete(_attrp_)  pthread_mutexattr_destroy(_attrp_)
#define pthread_condattr_create(_attrp_)   pthread_condattr_init(_attrp_)
#define pthread_condattr_delete(_attrp_)   pthread_condattr_destroy(_attrp_)  
#define pthread_attr_create(_attrp_)       pthread_attr_init(_attrp_)
#define pthread_attr_delete(_attrp_)       pthread_attr_destroy(_attrp_)
#endif /* FREEBSD || NETBSD */

#ifdef SOLARIS
typedef void	 (*pthread_initroutine_t)(void);
typedef void  *(*pthread_startroutine_t)(void *);
#define pthread_mutexattr_create(_attrp_)  pthread_mutexattr_init(_attrp_)
#define pthread_mutexattr_delete(_attrp_)  pthread_mutexattr_destroy(_attrp_)
#define pthread_condattr_create(_attrp_)   pthread_condattr_init(_attrp_)
#define pthread_condattr_delete(_attrp_)   pthread_condattr_destroy(_attrp_)  
#define pthread_attr_create(_attrp_)       pthread_attr_init(_attrp_)
#define pthread_attr_delete(_attrp_)       pthread_attr_destroy(_attrp_)
#endif /* SOLARIS */

#ifdef IRIX
typedef void	 (*pthread_initroutine_t)(void);
typedef void  *(*pthread_startroutine_t)(void *);
#define pthread_mutexattr_create(_attrp_)  pthread_mutexattr_init(_attrp_)
#define pthread_mutexattr_delete(_attrp_)  pthread_mutexattr_destroy(_attrp_)
#define pthread_condattr_create(_attrp_)   pthread_condattr_init(_attrp_)
#define pthread_condattr_delete(_attrp_)   pthread_condattr_destroy(_attrp_)  
#define pthread_attr_create(_attrp_)       pthread_attr_init(_attrp_)
#define pthread_attr_delete(_attrp_)       pthread_attr_destroy(_attrp_)
#endif /* IRIX */

/* Note that on Linux, we could be running with one of two different
   pthreads APIs.  The stock pthreads API that LinuxThreads and glibc2
   provide is compliant with the POSIX Pthreads Draft 7 spec.
   However, DCE is written to the (obsolete) Draft 4 spec, and the API
   changed considerably between Draft 4 and Draft 7.  If we are
   building with DCE, then we need to use the Draft 4 API as well, and
   the FreeDCE package for Linux includes a compatibility overlay that
   redefines the appropriate pthreads calls to look like Draft 4.

   This is *different* from the API changes introduced by the
   exception-handling pthreads interface that DCE uses.  On Linux, you
   can get that interface by including <dce/pthread_exc.h> instead of
   <pthread.h>.  We prefer to use the non-exception-handling
   interface. */

nasd_status_t nasd_sys_mutex_init(pthread_mutex_t *m);
nasd_status_t nasd_sys_mutex_destroy(pthread_mutex_t *m);
nasd_status_t nasd_sys_cond_init(pthread_cond_t *c);
nasd_status_t nasd_sys_cond_destroy(pthread_cond_t *c);

#define NASD_SYS_DECLARE_MUTEX(_m_)                pthread_mutex_t _m_;
#define NASD_SYS_DECLARE_STATIC_MUTEX(_m_)  static pthread_mutex_t _m_;
#define NASD_SYS_DECLARE_EXTERN_MUTEX(_m_)  extern pthread_mutex_t _m_;
#define NASD_SYS_DECLARE_COND(_c_)                 pthread_cond_t  _c_;
#define NASD_SYS_DECLARE_STATIC_COND(_c_)   static pthread_cond_t  _c_;
#define NASD_SYS_DECLARE_EXTERN_COND(_c_)   extern pthread_cond_t  _c_;

#define NASD_SYS_LOCK_MUTEX(_m_)     {pthread_mutex_lock(&(_m_));}
#define NASD_SYS_UNLOCK_MUTEX(_m_)   pthread_mutex_unlock(&(_m_))

#if (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)
#define NASD_SYS_TRY_LOCK_MUTEX(_m_) pthread_mutex_trylock(&(_m_))
#else /* (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE) */

#ifdef DEC_OSF

/*
 * Basically, all these ifdef's about the RPC package are to try
 * to guess which (hoser) pthreads draft we're using, because of
 * course operations have the same name but different return value
 * semantics. The *BEST* is pthread_mutex_trylock(), which under
 * draft 4 returns 0 indicating that the mutex is already locked,
 * but in draft 7 returns 0 indicating that the mutex lock was
 * taken successfully. Feel my warm, pulsating hate. The absolute
 * best part of all this is how there's no standard define for
 * determining which draft we're running with. Okay, here's the
 * brilliant part: getting this wrong will result in mysterious
 * hangs. I'm _this_ close to adding a sanity check to the drive
 * init that makes sure the try lock semantic is right. The current
 * symptom of guessing our pthreads draft wrong is that the drive
 * hangs during a format waiting for a refcount block to complete
 * its I/O. How cool is that?  --jimz
 */

#define NASD_SYS_TRY_LOCK_MUTEX(_m_) pthread_mutex_trylock(&(_m_))

#else /* DEC_OSF */

/* POSIX hosers.  draft 4 trylock (DUX, Linux w/ DCE wrappers) returns:
        1: successful completion, mutex locked
        0: unsuccessful; mutex already locked
       -1: mutex is not valid (EINVAL)
   draft 7 trylock (Linux w/o DCE wrappers) returns:
        0: successful completion
        EBUSY: mutex not acquired, currently locked by other thread
        EINVAL: invalid mutex
   we do not preserve complete draft 4 semantics here (we only
   distinguish between successful/unsuccessful, instead of propagating
   the error code back) because otherwise we'd need to define this
   as a function, which would be much slower than this macro. */
#define NASD_SYS_TRY_LOCK_MUTEX(_m_) ((pthread_mutex_trylock(&(_m_)) == 0) ? 1 : 0)

#endif /* DEC_OSF */

#endif /* (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE) */

#define NASD_SYS_WAIT_COND(_c_,_m_)  pthread_cond_wait( &(_c_), &(_m_) )
#define NASD_SYS_SIGNAL_COND(_c_)    pthread_cond_signal( &(_c_) )
#define NASD_SYS_BROADCAST_COND(_c_) pthread_cond_broadcast(&(_c_))

#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(IRIX)
#define NASD_DECLARE_ONCE(_o_) pthread_once_t _o_ = PTHREAD_ONCE_INIT;
#else /* LINUX || FREEBSD || NETBSD || IRIX */
#define NASD_DECLARE_ONCE(_o_) pthread_once_t _o_ = {0,0,0};
#endif /* LINUX || FREEBSD || NETBSD || IRIX */

extern int _nasd_once(pthread_once_t *once_block,
  pthread_initroutine_t init_routine, char *file, int line);
#define nasd_once(_o_,_i_) _nasd_once(_o_, (pthread_initroutine_t)_i_, __FILE__, __LINE__)

typedef pthread_t               nasd_sys_thread_t;
#ifdef DEC_OSF
typedef pthread_addr_t          nasd_threadarg_t;
#else /* DEC_OSF */
typedef void *                  nasd_threadarg_t;
#endif /* DEC_OSF */
typedef pthread_attr_t          nasd_sys_threadattr_t;

#ifdef DEC_OSF
#define NASD_EXIT_THREAD(_status_)                  pthread_exit( (pthread_addr_t) (_status_) )
#else /* DEC_OSF */
#define NASD_EXIT_THREAD(_status_)                  pthread_exit( (void *) (_status_) )
#endif /* DEC_OSF */

#if (defined(LINUX) && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE)) || defined(FREEBSD) || defined(NETBSD)
#define NASD_DELAY_THREAD(_secs_,_nsecs_) { \
  struct timeval _interval; \
  _interval.tv_sec = (_secs_); \
  _interval.tv_usec = (_nsecs_) / 1000; \
  select(1, NULL, NULL, NULL, &_interval); \
}
#else /* (LINUX && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE)) || FREEBSD || NETBSD */
#define NASD_DELAY_THREAD(_secs_,_nsecs_) { \
  struct timespec _interval; \
  _interval.tv_sec = (_secs_); \
  _interval.tv_nsec = (_nsecs_); \
  pthread_delay_np(&_interval); \
}
#endif /* LINUX && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE) */

typedef unsigned long       nasd_thread_id_t;
#define NASD_THREAD_ID_FMT  "lu"
#define NASD_THREAD_ID_NULL 0UL

#ifdef DEC_OSF
#define nasd_thread_self() ((nasd_thread_id_t)(pthread_self().field1))
#endif /* DEC_OSF */

#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS) || defined(IRIX)
#define nasd_thread_self() ((nasd_thread_id_t)(pthread_self()))
#endif /* LINUX || FREEBSD || NETBSD || SOLARIS || IRIX */

#if defined(LINUX) && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)
/* pthreads forgets to prototype these, so we do so here (to avoid warnings) */
extern int pthd4_setspecific(pthread_key_t key, pthread_addr_t value);
extern int pthd4_getspecific(pthread_key_t key, pthread_addr_t *value);
#endif /* LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE) */

/* XXX only works if already at toplevel */
#define NASD_THREAD_KILL_SELF() return

#endif /* !_NASD__NASD_THREADS_PTHREAD_H_ */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
