/*
 * nasd_threadstuff.h
 *
 * Generic threads and synchronization management and assistance
 *
 * Authors: Mark Holland, Daniel Stodolsky, Jim Zelenka
 */
/*
 * 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_THREADSTUFF_H_
#define _NASD_THREADSTUFF_H_

#include <nasd/nasd_options.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_types.h>

#ifdef SOLARIS
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* SOLARIS */

#ifdef AIX
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* AIX */

#ifdef LINUX
#ifdef KERNEL
#include <nasd/linux/nasd_linux_threads_kernel.h>
#define NASD_SYS_THREADS_DEFINED 1
#else /* KERNEL */
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* KERNEL */
#endif /* LINUX */

#ifdef DEC_OSF
#ifdef KERNEL
#include <nasd/dux/nasd_dux_threads_kernel.h>
#define NASD_SYS_THREADS_DEFINED 1
#else /* KERNEL */
#if NASD_PTHREAD_EXC > 0
#include <pthread_exc.h>
#else /* NASD_PTHREAD_EXC > 0 */
#include <pthread.h>
#endif /* NASD_PTHREAD_EXC > 0 */
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* KERNEL */
#endif /* DEC_OSF */

#ifdef FREEBSD
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* FREEBSD */

#ifdef NETBSD
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* NETBSD */

#ifdef IRIX
#include <pthread.h>
#include <nasd/nasd_threads_pthread.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* IRIX */


#if NASD_WIND_THREADS > 0
/* WindRiver/VxWorks threading & synchronization */
#include <nasd/vxworks/nasd_vx_threads.h>
#define NASD_SYS_THREADS_DEFINED 1
#endif /* NASD_WIND_THREADS > 0 */

#if NASD_SYS_THREADS_DEFINED == 0
#error "Don't know how to do threads on your platform"
#endif /* NASD_SYS_THREADS_DEFINED == 0 */

nasd_status_t nasd_threads_real_init(void);
void nasd_threads_real_shutdown(void);
void nasd_threadsys_init(void);
nasd_status_t nasd_threads_init(void);
void nasd_threads_shutdown(void);
nasd_status_t nasd_sys_threads_init(void);
nasd_status_t nasd_sys_threads_init_once(void);
void nasd_sys_threads_shutdown(void);

/*
 * Definitions below here are based entirely on the defined
 * system threads interface.
 */

#if NASD_DEBUG_ATOMIC > 0

#define NASD_ATENT_M 1
#define NASD_ATENT_C 2

typedef struct NASD_ATEnt_s NASD_ATEnt_t;

struct NASD_ATEnt_s {
  char              *file;
  int                line;
  char              *lk_file;
  int                lk_line;
  nasd_thread_id_t   lk_thread;
  NASD_SYS_DECLARE_MUTEX(m)
  NASD_SYS_DECLARE_COND(c)
  int                type;
  int                otype;
  NASD_ATEnt_t      *next;
  NASD_ATEnt_t      *prev;
};

#define NASD_DECLARE_MUTEX(_m_)                NASD_ATEnt_t *_m_;
#define NASD_DECLARE_STATIC_MUTEX(_m_)  static NASD_ATEnt_t *_m_;
#define NASD_DECLARE_EXTERN_MUTEX(_m_)  extern NASD_ATEnt_t *_m_;
#define NASD_DECLARE_COND(_c_)                 NASD_ATEnt_t *_c_;
#define NASD_DECLARE_STATIC_COND(_c_)   static NASD_ATEnt_t *_c_;
#define NASD_DECLARE_EXTERN_COND(_c_)   extern NASD_ATEnt_t *_c_;

#define NASD_MUTEX_SYS_OF(_m_)                 ((_m_)->m)
#define NASD_COND_SYS_OF(_c_)                  ((_m_)->c)

nasd_status_t _nasd_mutex_init(NASD_ATEnt_t **m, char *file, int line);
nasd_status_t _nasd_mutex_destroy(NASD_ATEnt_t **m, char *file, int line);
nasd_status_t _nasd_cond_init(NASD_ATEnt_t **c, char *file, int line);
nasd_status_t _nasd_cond_destroy(NASD_ATEnt_t **c, char *file, int line);
void nasd_atent_init(void);
void nasd_atent_shutdown(void);

#define nasd_mutex_init(_m_) _nasd_mutex_init(_m_,__FILE__,__LINE__)
#define nasd_mutex_destroy(_m_) _nasd_mutex_destroy(_m_,__FILE__,__LINE__)
#define nasd_cond_init(_m_) _nasd_cond_init(_m_,__FILE__,__LINE__)
#define nasd_cond_destroy(_m_) _nasd_cond_destroy(_m_,__FILE__,__LINE__)

#define NASD_MUTEX_OWNED(_a_) (((_a_)->lk_thread)==nasd_thread_self())
#define NASD_MUTEX_NOT_OWNED(_a_) (((_a_)->lk_thread)!=nasd_thread_self())

#define __NASD_LOCK_MUTEX_SET_LOCK_DATA(_m_) { \
  (_m_)->lk_file = __FILE__; \
  (_m_)->lk_line = __LINE__; \
  (_m_)->lk_thread = nasd_thread_self(); \
}

#define __NASD_LOCK_MUTEX_CLEAR_LOCK_DATA(_m_) { \
  (_m_)->lk_file = NULL; \
  (_m_)->lk_line = 0; \
  (_m_)->lk_thread = 0; \
  }

int  _nasd_debugging_trylock_mutex(NASD_ATEnt_t *atent, char *file, int line);

#define NASD_LOCK_MUTEX(_a_) { \
  NASD_ASSERT((_a_)->type == NASD_ATENT_M); \
  if (NASD_MUTEX_OWNED((_a_))) { \
    nasd_printf("NASD DEBUG ATOMIC: double-lock of mutex (%s:%d) at %s:%d\n", \
                (_a_)->file, (_a_)->line, __FILE__, __LINE__); \
    nasd_printf("NASD DEBUG ATOMIC (cont.): current thread is %ld.\n", \
                nasd_thread_self()); \
    NASD_PANIC(); \
  } \
  NASD_SYS_LOCK_MUTEX((_a_)->m); \
  __NASD_LOCK_MUTEX_SET_LOCK_DATA((_a_)); \
}

#define NASD_TRY_LOCK_MUTEX(_a_) \
  _nasd_debugging_trylock_mutex((_a_), __FILE__, __LINE__)

#define NASD_UNLOCK_MUTEX(_a_) { \
  NASD_ASSERT((_a_)->type == NASD_ATENT_M); \
  if (NASD_MUTEX_NOT_OWNED((_a_))) { \
    nasd_printf("NASD DEBUG ATOMIC: double-unlock of mutex (%s:%d) at " \
                "%s:%d\n", (_a_)->file, (_a_)->line, __FILE__, __LINE__); \
    if ((_a_)->lk_thread == 0) { \
      nasd_printf("NASD DEBUG ATOMIC (cont.): current thread is %ld, lock " \
                  "was unheld.\n", nasd_thread_self()); \
    } else { \
      nasd_printf("NASD DEBUG ATOMIC (cont.): current thread is %ld, but " \
                  "thread is locked by %ld.\n", nasd_thread_self(), \
                  (_a_)->lk_thread); \
    } \
    NASD_PANIC(); \
  } \
  __NASD_LOCK_MUTEX_CLEAR_LOCK_DATA((_a_)); \
  NASD_SYS_UNLOCK_MUTEX((_a_)->m); \
}

#define NASD_WAIT_COND(_c_,_m_) { \
  NASD_ASSERT((_c_)->type == NASD_ATENT_C); \
  NASD_ASSERT((_m_)->type == NASD_ATENT_M); \
  if (NASD_MUTEX_NOT_OWNED((_m_))) { \
    nasd_printf("NASD DEBUG ATOMIC: wait on unlocked mutex (%s:%d) at %s:%d\n", \
                (_m_)->file, (_m_)->line, __FILE__, __LINE__); \
    NASD_PANIC(); \
  } \
  NASD_SYS_WAIT_COND((_c_)->c, (_m_)->m); \
  __NASD_LOCK_MUTEX_SET_LOCK_DATA((_m_)); \
}

#define NASD_SIGNAL_COND(_c_) { \
  NASD_ASSERT((_c_)->type == NASD_ATENT_C); \
  NASD_SYS_SIGNAL_COND((_c_)->c); \
}

#define NASD_BROADCAST_COND(_c_) { \
  NASD_ASSERT((_c_)->type == NASD_ATENT_C); \
  NASD_SYS_BROADCAST_COND((_c_)->c); \
}

#else /* NASD_DEBUG_ATOMIC > 0 */

#define NASD_DECLARE_MUTEX(_m_)         NASD_SYS_DECLARE_MUTEX(_m_)
#define NASD_DECLARE_STATIC_MUTEX(_m_)  NASD_SYS_DECLARE_STATIC_MUTEX(_m_)
#define NASD_DECLARE_EXTERN_MUTEX(_m_)  NASD_SYS_DECLARE_EXTERN_MUTEX(_m_)
#define NASD_DECLARE_COND(_c_)          NASD_SYS_DECLARE_COND(_c_)
#define NASD_DECLARE_STATIC_COND(_c_)   NASD_SYS_DECLARE_STATIC_COND(_c_)
#define NASD_DECLARE_EXTERN_COND(_c_)   NASD_SYS_DECLARE_EXTERN_COND(_c_)

#define NASD_MUTEX_SYS_OF(_m_)                 _m_
#define NASD_COND_SYS_OF(_c_)                  _c_

#define NASD_LOCK_MUTEX(_m_)      NASD_SYS_LOCK_MUTEX(_m_)
#define NASD_UNLOCK_MUTEX(_m_)    NASD_SYS_UNLOCK_MUTEX(_m_)
#define NASD_MUTEX_OWNED(_a_)     1 /* only good for debugging */
#define NASD_MUTEX_NOT_OWNED(_a_) 1 /* only good for debugging */

#define NASD_TRY_LOCK_MUTEX(_m_)  NASD_SYS_TRY_LOCK_MUTEX(_m_) /* nonzero = lock taken */
#define NASD_WAIT_COND(_c_,_m_)   NASD_SYS_WAIT_COND(_c_,_m_)
#define NASD_SIGNAL_COND(_c_)     NASD_SYS_SIGNAL_COND(_c_)
#define NASD_BROADCAST_COND(_c_)  NASD_SYS_BROADCAST_COND(_c_)

#define nasd_mutex_init(_mp_)     nasd_sys_mutex_init(_mp_)
#define nasd_mutex_destroy(_mp_)  nasd_sys_mutex_destroy(_mp_)
#define nasd_cond_init(_cp_)      nasd_sys_cond_init(_cp_)
#define nasd_cond_destroy(_cp_)   nasd_sys_cond_destroy(_cp_)

#endif /* NASD_DEBUG_ATOMIC > 0 */

/*
 * Basic thread management
 */
typedef nasd_sys_thread_t      nasd_thread_t;
typedef nasd_sys_threadattr_t  nasd_threadattr_t;

typedef void (*nasd_threadfunc_t)(nasd_threadarg_t);

#define nasd_threadattr_create(_attrp_) \
  nasd_sys_threadattr_create(_attrp_)
#define nasd_threadattr_destroy(_attrp_) \
  nasd_sys_threadattr_destroy(_attrp_)
#define nasd_threadattr_setstacksize(_attrp_,_size_) \
  nasd_sys_threadattr_setstacksize(_attrp_,_size_)
#define nasd_threadattr_getstacksize(_attrp_,_sizep_) \
  nasd_sys_threadattr_getstacksize(_attrp_,_sizep_)
#define nasd_thread_create_attr(_threadp_,_attrp_,_func_,_arg_) \
  nasd_sys_thread_create_attr(_threadp_,_attrp_,_func_,_arg_)
#define nasd_thread_create(_threadp_,_func_,_arg_) \
  nasd_sys_thread_create(_threadp_,_func_,_arg_)
#define nasd_thread_create_w_name(_threadp_,_func_,_arg_,_name_) \
  nasd_sys_thread_create_w_name(_threadp_,_func_,_arg_,_name_)

/*
 * Thread management defined by system module
 */
nasd_status_t nasd_sys_threadattr_create(nasd_sys_threadattr_t *attrp);
nasd_status_t nasd_sys_threadattr_destroy(nasd_sys_threadattr_t *attrp);
nasd_status_t nasd_sys_threadattr_setstacksize(nasd_sys_threadattr_t *attrp,
  int size);
nasd_status_t nasd_sys_threadattr_getstacksize(nasd_sys_threadattr_t *attrp,
  int *sizep);
nasd_status_t nasd_sys_thread_create_attr(nasd_sys_thread_t *threadp,
  nasd_sys_threadattr_t *attrp, nasd_threadfunc_t func,
  nasd_threadarg_t arg);
nasd_status_t nasd_sys_thread_create(nasd_sys_thread_t *threadp,
  nasd_threadfunc_t func, nasd_threadarg_t arg);
nasd_status_t nasd_sys_thread_create_w_name(nasd_sys_thread_t *threadp,
  nasd_threadfunc_t func, nasd_threadarg_t arg, char *name);

/*
 * Thread management- sync start/stop of groups of threads
 */

struct nasd_threadgroup_s {
  int  created;
  int  running;
  int  shutdown;
  int  should_shutdown;
  NASD_DECLARE_MUTEX(mutex)
  NASD_DECLARE_COND(cond)
};

#define nasd_init_threadgroup(a) _nasd_init_threadgroup(a,__FILE__,__LINE__)
#define nasd_destroy_threadgroup(a) _nasd_destroy_threadgroup(a,__FILE__,__LINE__)

nasd_status_t _nasd_init_threadgroup(nasd_threadgroup_t *g, char *file, int line);
nasd_status_t _nasd_destroy_threadgroup(nasd_threadgroup_t *g, char *file, int line);

/*
 * Someone has started a thread in the group
 */
#define NASD_THREADGROUP_STARTED(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	(_g_)->created++; \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
}

/*
 * Thread announcing that it is now running
 */
#define NASD_THREADGROUP_RUNNING(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	(_g_)->running++; \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
	NASD_SIGNAL_COND((_g_)->cond); \
}

/*
 * Thread announcing that it is now done
 */
#define NASD_THREADGROUP_DONE(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	(_g_)->shutdown++; \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
	NASD_SIGNAL_COND((_g_)->cond); \
}

/*
 * Wait for all threads to start running
 */
#define NASD_THREADGROUP_WAIT_START(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	while((_g_)->running < (_g_)->created) { \
		NASD_WAIT_COND((_g_)->cond, (_g_)->mutex); \
	} \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
}

/*
 * Wait for all threads to stop running
 */
#define NASD_THREADGROUP_WAIT_STOP(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	NASD_ASSERT((_g_)->running == (_g_)->created); \
	(_g_)->should_shutdown = 1; \
	while((_g_)->shutdown < (_g_)->running) { \
		NASD_WAIT_COND((_g_)->cond, (_g_)->mutex); \
	} \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
}

#define NASD_THREADGROUP_INDICATE_SHUTDOWN(_g_) { \
	NASD_LOCK_MUTEX((_g_)->mutex); \
	(_g_)->should_shutdown = 1; \
	NASD_UNLOCK_MUTEX((_g_)->mutex); \
}

#define NASD_THREADGROUP_SHUTDOWNP(_g_) (_g_)->should_shutdown

/*
 * Support for readers/writers
 *
 * Where applicable, use system-provided, otherwise, use our
 * own cheezy ones
 */

#if NASD_TS_RWLOCK_DECLARED == 0
/*
 * If the system port hasn't defined readers/writers
 * locks for us, then we'll just have to use our own.
 * These are kind of stinky.
 */
#define NASD_TS_RWLOCK_DECLARED 1
#define NASD_TS_CHEESY_RWLOCKS 1
typedef struct nasd_rwlock_s nasd_rwlock_t;
struct nasd_rwlock_s {
  NASD_DECLARE_MUTEX(lock)
  NASD_DECLARE_COND(cond)
  NASD_DECLARE_COND(wcond)
  char  *wr_file;
  int    wr_line;
  char  *rd_file;
  int    rd_line;
  int    nreaders;
  int    nwriters_waiting;
  int    nreaders_waiting;
  int    writing;
};

#define NASD_DECLARE_RWLOCK(_name_) nasd_rwlock_t _name_ ;
#define NASD_LOCK_READ(_l_)     nasd_rwlock_read(&(_l_), __FILE__, __LINE__)
#define NASD_UNLOCK_READ(_l_)   nasd_rwlock_read_done(&(_l_), __FILE__, __LINE__)
#define NASD_LOCK_WRITE(_l_)    nasd_rwlock_write(&(_l_), __FILE__, __LINE__)
#define NASD_UNLOCK_WRITE(_l_)  nasd_rwlock_write_done(&(_l_), __FILE__, __LINE__)
#define nasd_rwlock_init(_l_)    _nasd_rwlock_init(_l_, __FILE__, __LINE__);
#define nasd_rwlock_destroy(_l_) _nasd_rwlock_destroy(_l_, __FILE__, __LINE__);

extern void nasd_rwlock_read(nasd_rwlock_t *l, char *file, int line);
extern void nasd_rwlock_read_done(nasd_rwlock_t *l, char *file, int line);
extern void nasd_rwlock_write(nasd_rwlock_t *l, char *file, int line);
extern void nasd_rwlock_write_done(nasd_rwlock_t *l, char *file, int line);
extern nasd_status_t _nasd_rwlock_init(nasd_rwlock_t *l, char *file, int line);
extern nasd_status_t _nasd_rwlock_destroy(nasd_rwlock_t *l, char *file,
  int line);
#endif /* NASD_TS_RWLOCK_DECLARED == 0 */

#endif /* !_NASD_THREADSTUFF_H_ */
