/*
 * nasd_pthread_support.c
 *
 * pthread support routines
 *
 * 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.
 */


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

pthread_mutexattr_t nasd_pthread_mutex_attr;
pthread_condattr_t nasd_pthread_cond_attr;
int nasd_pthread_mutex_attr_good = 0;
int nasd_pthread_cond_attr_good = 0;
#if defined(SOLARIS) || defined(NETBSD)
int nasd_pthread_thread_attr_good = 0;
pthread_attr_t nasd_pthread_thread_attr;
#endif /* SOLARIS || NETBSD */

nasd_status_t
nasd_sys_threads_init()
{
  /* nothing to do here */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threads_init_once()
{
  int ret;

  nasd_pthread_mutex_attr_good = 0;
  nasd_pthread_cond_attr_good = 0;
#ifdef SOLARIS
  nasd_pthread_thread_attr_good = 0;
#endif /* SOLARIS */

#ifdef PTHREAD_EXC
  pthread_mutexattr_create(&nasd_pthread_mutex_attr);
  pthread_mutexattr_setkind_np(&nasd_pthread_mutex_attr, MUTEX_FAST_NP);
  pthread_condattr_create(&nasd_pthread_cond_attr);
#else /* PTHREAD_EXC */
  ret = pthread_mutexattr_create(&nasd_pthread_mutex_attr);
  if (ret)
    return(NASD_FAIL);
#if !defined(FREEBSD) && !defined(SOLARIS) && !defined(IRIX) && !defined(LINUX) && !defined(NETBSD)
  /* XXX - RH61 port */
  /* not supported by FreeBSD's threads library */
  ret = pthread_mutexattr_setkind_np(&nasd_pthread_mutex_attr, MUTEX_FAST_NP);
  if (ret) {
    pthread_mutexattr_delete(&nasd_pthread_mutex_attr);
    return(NASD_FAIL);
  }
#endif /* !FREEBSD && !SOLARIS && !IRIX && !NETBSD */
  ret = pthread_condattr_create(&nasd_pthread_cond_attr);
  if (ret) {
    pthread_mutexattr_delete(&nasd_pthread_mutex_attr);
    return(NASD_FAIL);
  }
#endif /* PTHREAD_EXC */
  nasd_pthread_mutex_attr_good = 1;
  nasd_pthread_cond_attr_good = 1;

#ifdef SOLARIS
  ret = pthread_attr_init(&nasd_pthread_thread_attr);
  if (ret)
    return(NASD_FAIL);
  ret = pthread_attr_setscope(&nasd_pthread_thread_attr,
    PTHREAD_SCOPE_SYSTEM);
  if (ret)
    return(NASD_FAIL);
  ret = pthread_attr_setdetachstate(&nasd_pthread_thread_attr,
    PTHREAD_CREATE_DETACHED);
  if (ret)
    return(NASD_FAIL);
  nasd_pthread_thread_attr_good = 1;
#endif /* SOLARIS */

  return(NASD_SUCCESS);
}

void
nasd_sys_threads_shutdown()
{
  /* nothing to do here */
}

nasd_status_t
nasd_sys_mutex_init(
  pthread_mutex_t  *m)
{
  int rc;

  /* the nice thing about standards is that there are so many to
     choose from. */
#ifdef PTHREAD_EXC
  if (nasd_pthread_mutex_attr_good == 0)
    return(NASD_FAIL);
  pthread_mutex_init(m, nasd_pthread_mutex_attr);
#else /* PTHREAD_EXC */
#if defined(DEC_OSF) || (defined(LINUX) && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE))
  if (nasd_pthread_mutex_attr_good == 0)
    return(NASD_FAIL);
  rc = pthread_mutex_init(m, nasd_pthread_mutex_attr);
  if (rc)
    return(NASD_FAIL);
#else /* DEC_OSF || (LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)) */
#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX) || defined(NETBSD)
  if (nasd_pthread_mutex_attr_good == 0)
    return(NASD_FAIL);
  rc = pthread_mutex_init(m, &nasd_pthread_mutex_attr);
  if (rc)
    return(NASD_FAIL);
#else /* LINUX || FREEBSD || SOLARIS || IRIX || NETBSD */
#ifdef AIX
	rc = pthread_mutex_init(m, &pthread_mutexattr_default);
  if (rc)
    return(NASD_FAIL);
#else
#error "don't know how to call pthread_mutex_init on your platform"
#endif /* AIX */
#endif /* LINUX || FREEBSD || SOLARIS || IRIX || NETBSD */
#endif /* DEC_OSF || (LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)) */
#endif /* PTHREAD_EXC */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_mutex_destroy(
  pthread_mutex_t  *m)
{
  int rc;

#ifdef PTHREAD_EXC
  pthread_mutex_destroy(m);
#else
  rc = pthread_mutex_destroy(m);
  if (rc)
    return(NASD_FAIL);
#endif /* PTHREAD_EXC */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_cond_init(
  pthread_cond_t  *c)
{
  int rc;

  /*
   * The *BEST* thing about using pthreads is how it gives you one
   * portable threads API. Really!
   */

#ifdef PTHREAD_EXC
  pthread_cond_init(c, pthread_condattr_default);
#else /* PTHREAD_EXC */
#ifdef DEC_OSF
  rc = pthread_cond_init(c, pthread_condattr_default);
#else /* DEC_OSF */
#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX) || defined(NETBSD)
  if (nasd_pthread_cond_attr_good == 0)
    return(NASD_FAIL);
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
  rc = pthread_cond_init(c, nasd_pthread_cond_attr);
#else /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
  rc = pthread_cond_init(c, &nasd_pthread_cond_attr);
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
#else /* LINUX || FREEBSD || SOLARIS || IRIX || NETBSD */
#ifdef AIX
  rc = pthread_cond_init(c, &pthread_condattr_default);
#else /* AIX */
#error "don't know how to call pthread_cond_init on your platform"
#endif /* AIX */
#endif /* LINUX || FREEBSD || SOLARIS || IRIX || NETBSD */
#endif /* DEC_OSF */
  if (rc)
    return(NASD_FAIL);
#endif /* PTHREAD_EXC */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_cond_destroy(
  pthread_cond_t  *c)
{
  int rc;

#ifdef PTHREAD_EXC
  pthread_cond_destroy(c);
#else /* PTHREAD_EXC */
  rc = pthread_cond_destroy(c);
  if (rc)
    return(NASD_FAIL);
#endif /* PTHREAD_EXC */
  return(NASD_SUCCESS);
}

int
_nasd_once(
  pthread_once_t         *once_block,
  pthread_initroutine_t   init_routine,
  char                   *file,
  int                     line)
{
  int rc = NASD_SUCCESS;

#ifdef PTHREAD_EXC
  pthread_once(once_block, init_routine);
#else /* PTHREAD_EXC */
  rc = pthread_once(once_block, init_routine);
#endif /* PTHREAD_EXC */
  return(rc);
}

nasd_status_t
nasd_sys_threadattr_create(
  nasd_sys_threadattr_t  *attrp)
{
  int ret;

#ifdef SOLARIS
  ret = pthread_attr_init(&nasd_pthread_thread_attr);
  if (ret)
    return(NASD_FAIL);
  ret = pthread_attr_setscope(&nasd_pthread_thread_attr,
    PTHREAD_SCOPE_SYSTEM);
  if (ret) {
    pthread_attr_destroy(&nasd_pthread_thread_attr);
    return(NASD_FAIL);
  }
  ret = pthread_attr_setdetachstate(&nasd_pthread_thread_attr,
    PTHREAD_CREATE_DETACHED);
  if (ret) {
    pthread_attr_destroy(&nasd_pthread_thread_attr);
    return(NASD_FAIL);
  }
#else /* SOLARIS */
  ret = pthread_attr_create(attrp);
  if (ret)
    return(NASD_FAIL);
#endif /* SOLARIS */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threadattr_destroy(
  nasd_sys_threadattr_t  *attrp)
{
  int ret;

  ret = pthread_attr_delete(attrp);
  if (ret)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

#ifdef LINUX

/* 
 * On Linux, the stack size is set to 16K initially and grows on
 * demand, up to a fixed maximum of 2MB.  Thus, setstacksize is not
 * supported and getstacksize returns a constant value.
 */

nasd_status_t
nasd_sys_threadattr_setstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                     size)
{
  return(NASD_OP_NOT_SUPPORTED);
}

nasd_status_t
nasd_sys_threadattr_getstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                    *sizep)
{
  /* @@@ we have to hardcode this, because apparently it isn't
     specified in any public header files.  c.f. STACK_SIZE in
     glibc-2.0.7/linuxthreads/internals.h. */
  *sizep = 2*1024*1024;
  return(NASD_SUCCESS);
}

#else /* LINUX */

nasd_status_t
nasd_sys_threadattr_setstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                     size)
{
  int ret;

  ret = pthread_attr_setstacksize(attrp, (long)size);
  if (ret)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threadattr_getstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                    *sizep)
{
#ifdef IRIX
  size_t size;
  int ret;

  ret = pthread_attr_getstacksize(attrp, &size);
  if (ret)
     return(NASD_FAIL);
  *sizep = size;
  return(NASD_SUCCESS);
#else /* IRIX */
#if defined(FREEBSD) || defined(SOLARIS) || defined(NETBSD)
  int ret;

  ret = pthread_attr_getstacksize(attrp, sizep);
  if (ret != 0)
    return NASD_FAIL;
  return NASD_SUCCESS;
#else /* FREEBSD || SOLARIS || NETBSD */
  long size;

  size = pthread_attr_getstacksize(*attrp);
  if (size < 0)
    return(NASD_FAIL);
  *sizep = (int)size;
  return(NASD_SUCCESS);
#endif /* FREEBSD || SOLARIS || NETBSD */
#endif /* IRIX */
}

#endif /* LINUX */

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)
{
  int ret;

#if defined(DEC_OSF) || (defined(LINUX) && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE))
  ret = pthread_create(threadp, *attrp, (pthread_startroutine_t)func, arg);
#else /* DEC_OSF || (LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)) */
  ret = pthread_create(threadp, attrp, (pthread_startroutine_t)func, arg);
#endif /* DEC_OSF || (LINUX && (NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE)) */
  if (ret)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_thread_create(
  nasd_sys_thread_t  *threadp,
  nasd_threadfunc_t   func,
  nasd_threadarg_t    arg)
{
  int ret;

#if defined(LINUX) && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE)
  ret = pthread_create(threadp, NULL,
    (pthread_startroutine_t)func, arg);
#else /* LINUX && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE) */
#  if defined(SOLARIS) || defined(NETBSD)
  if (nasd_pthread_thread_attr_good == 0)
    return(NASD_FAIL);
  ret = pthread_create(threadp, &nasd_pthread_thread_attr,
    (pthread_startroutine_t)func, arg);
#  else /* SOLARIS || NETBSD */
#    ifdef IRIX
  ret = pthread_create(threadp, NULL,
    (pthread_startroutine_t)func, arg);
#    else /* IRIX */
  ret = pthread_create(threadp, pthread_attr_default,
    (pthread_startroutine_t)func, arg);
#    endif /* IRIX */
#  endif /* SOLARIS || NETBSD */
#endif /* LINUX && (NASD_RPC_PACKAGE != NASD_RPC_PACKAGE_DCE) */
  if (ret)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_thread_create_w_name(
  nasd_sys_thread_t  *threadp,
  nasd_threadfunc_t   func,
  nasd_threadarg_t    arg,
  char               *name)
{
  nasd_status_t rc;

  /* "Sorry" */
  rc = nasd_sys_thread_create(threadp, func, arg);
  return(rc);
}

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