Blame src/ath.c

Packit 0680ba
/* ath.c - A Thread-safeness library.
Packit 0680ba
 *  Copyright (C) 2002, 2003, 2004, 2011 Free Software Foundation, Inc.
Packit 0680ba
 *  Copyright (C) 2014 g10 Code GmbH
Packit 0680ba
 *
Packit 0680ba
 * This file is part of Libgcrypt.
Packit 0680ba
 *
Packit 0680ba
 * Libgcrypt is free software; you can redistribute it and/or modify
Packit 0680ba
 * it under the terms of the GNU Lesser general Public License as
Packit 0680ba
 * published by the Free Software Foundation; either version 2.1 of
Packit 0680ba
 * the License, or (at your option) any later version.
Packit 0680ba
 *
Packit 0680ba
 * Libgcrypt is distributed in the hope that it will be useful,
Packit 0680ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0680ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0680ba
 * GNU Lesser General Public License for more details.
Packit 0680ba
 *
Packit 0680ba
 * You should have received a copy of the GNU Lesser General Public
Packit 0680ba
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit 0680ba
 */
Packit 0680ba
Packit 0680ba
#ifdef HAVE_CONFIG_H
Packit 0680ba
#include <config.h>
Packit 0680ba
#endif
Packit 0680ba
Packit 0680ba
#include <assert.h>
Packit 0680ba
#include <stdlib.h>
Packit 0680ba
#include <unistd.h>
Packit 0680ba
#include <errno.h>
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
# include <pthread.h>
Packit 0680ba
#endif
Packit 0680ba
Packit 0680ba
#include "ath.h"
Packit 0680ba
Packit 0680ba
#if USE_POSIX_THREADS_WEAK
Packit 0680ba
# if  !USE_POSIX_THREADS
Packit 0680ba
#  error USE_POSIX_THREADS_WEAK but no USE_POSIX_THREADS
Packit 0680ba
# endif
Packit 0680ba
#endif
Packit 0680ba
Packit 0680ba

Packit 0680ba
/* On an ELF system it is easy to use pthreads using weak references.
Packit 0680ba
   Take care not to test the address of a weak referenced function we
Packit 0680ba
   actually use; some GCC versions have a bug were &foo != NULL is
Packit 0680ba
   always evaluated to true in PIC mode.  USING_PTHREAD_AS_DEFAULT is
Packit 0680ba
   used by ath_install to detect the default usage of pthread.  */
Packit 0680ba
#if USE_POSIX_THREADS_WEAK
Packit 0680ba
# pragma weak pthread_cancel
Packit 0680ba
# pragma weak pthread_mutex_init
Packit 0680ba
# pragma weak pthread_mutex_lock
Packit 0680ba
# pragma weak pthread_mutex_unlock
Packit 0680ba
# pragma weak pthread_mutex_destroy
Packit 0680ba
#endif
Packit 0680ba
Packit 0680ba
/* For the dummy interface.  The MUTEX_NOTINIT value is used to check
Packit 0680ba
   that a mutex has been initialized.  */
Packit 0680ba
#define MUTEX_NOTINIT	((ath_mutex_t) 0)
Packit 0680ba
#define MUTEX_UNLOCKED	((ath_mutex_t) 1)
Packit 0680ba
#define MUTEX_LOCKED	((ath_mutex_t) 2)
Packit 0680ba
#define MUTEX_DESTROYED	((ath_mutex_t) 3)
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Return the thread type from the option field. */
Packit 0680ba
#define GET_OPTION(a)    ((a) & 0xff)
Packit 0680ba
Packit 0680ba
Packit 0680ba

Packit 0680ba
enum ath_thread_model {
Packit 0680ba
  ath_model_undefined = 0,
Packit 0680ba
  ath_model_none,          /* No thread support.  */
Packit 0680ba
  ath_model_pthreads_weak, /* POSIX threads using weak symbols.  */
Packit 0680ba
  ath_model_pthreads,      /* POSIX threads directly linked.  */
Packit 0680ba
  ath_model_w32            /* Microsoft Windows threads.  */
Packit 0680ba
};
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* The thread model in use.  */
Packit 0680ba
static enum ath_thread_model thread_model;
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Initialize the ath subsystem.  This is called as part of the
Packit 0680ba
   Libgcrypt initialization.  It's purpose is to initialize the
Packit 0680ba
   locking system.  It returns 0 on sucess or an ERRNO value on error.
Packit 0680ba
   In the latter case it is not defined whether ERRNO was changed.
Packit 0680ba
Packit 0680ba
   Note: This should be called as early as possible because it is not
Packit 0680ba
   always possible to detect the thread model to use while already
Packit 0680ba
   running multi threaded.  */
Packit 0680ba
int
Packit 0680ba
ath_init (void)
Packit 0680ba
{
Packit 0680ba
  int err = 0;
Packit 0680ba
Packit 0680ba
  if (thread_model)
Packit 0680ba
    return 0; /* Already initialized - no error.  */
Packit 0680ba
Packit 0680ba
  if (0)
Packit 0680ba
    ;
Packit 0680ba
#if USE_POSIX_THREADS_WEAK
Packit 0680ba
  else if (pthread_cancel)
Packit 0680ba
    {
Packit 0680ba
      thread_model = ath_model_pthreads_weak;
Packit 0680ba
    }
Packit 0680ba
#endif
Packit 0680ba
  else
Packit 0680ba
    {
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
      thread_model = ath_model_w32;
Packit 0680ba
#elif USE_POSIX_THREADS && !USE_POSIX_THREADS_WEAK
Packit 0680ba
      thread_model = ath_model_pthreads;
Packit 0680ba
#else /*!USE_POSIX_THREADS*/
Packit 0680ba
      /* Assume a single threaded application.  */
Packit 0680ba
      thread_model = ath_model_none;
Packit 0680ba
#endif /*!USE_POSIX_THREADS*/
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  return err;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Return the used thread model as string for display purposes an if
Packit 0680ba
   R_MODEL is not null store its internal number at R_MODEL.  */
Packit 0680ba
const char *
Packit 0680ba
ath_get_model (int *r_model)
Packit 0680ba
{
Packit 0680ba
  if (r_model)
Packit 0680ba
    *r_model = thread_model;
Packit 0680ba
  switch (thread_model)
Packit 0680ba
    {
Packit 0680ba
    case ath_model_undefined:     return "undefined";
Packit 0680ba
    case ath_model_none:          return "none";
Packit 0680ba
    case ath_model_pthreads_weak: return "pthread(weak)";
Packit 0680ba
    case ath_model_pthreads:      return "pthread";
Packit 0680ba
    case ath_model_w32:           return "w32";
Packit 0680ba
    default:                      return "?";
Packit 0680ba
    }
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* This function was used in old Libgcrypt versions (via
Packit 0680ba
   GCRYCTL_SET_THREAD_CBS) to register the thread callback functions.
Packit 0680ba
   It is not anymore required.  However to allow existing code to
Packit 0680ba
   continue to work, we keep this function and check that no user
Packit 0680ba
   defined callbacks are used and that the requested thread system
Packit 0680ba
   matches the one Libgcrypt is using.  */
Packit 0680ba
gpg_err_code_t
Packit 0680ba
ath_install (struct ath_ops *ath_ops)
Packit 0680ba
{
Packit 0680ba
  gpg_err_code_t rc;
Packit 0680ba
  unsigned int thread_option;
Packit 0680ba
Packit 0680ba
  /* Fist call ath_init so that we know our thread model.  */
Packit 0680ba
  rc = ath_init ();
Packit 0680ba
  if (rc)
Packit 0680ba
    return rc;
Packit 0680ba
Packit 0680ba
  /* Check if the requested thread option is compatible to the
Packit 0680ba
     thread option we are already committed to.  */
Packit 0680ba
  thread_option = ath_ops? GET_OPTION (ath_ops->option) : 0;
Packit 0680ba
Packit 0680ba
  /* Return an error if the requested thread model does not match the
Packit 0680ba
     configured one.  */
Packit 0680ba
  if (0)
Packit 0680ba
    ;
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
  else if (thread_model == ath_model_pthreads
Packit 0680ba
           || thread_model == ath_model_pthreads_weak)
Packit 0680ba
    {
Packit 0680ba
      if (thread_option == ATH_THREAD_OPTION_PTHREAD)
Packit 0680ba
        return 0; /* Okay - compatible.  */
Packit 0680ba
      if (thread_option == ATH_THREAD_OPTION_PTH)
Packit 0680ba
        return 0; /* Okay - compatible.  */
Packit 0680ba
    }
Packit 0680ba
#endif /*USE_POSIX_THREADS*/
Packit 0680ba
  else if (thread_option == ATH_THREAD_OPTION_PTH)
Packit 0680ba
    {
Packit 0680ba
      if (thread_model == ath_model_none)
Packit 0680ba
        return 0; /* Okay - compatible.  */
Packit 0680ba
    }
Packit 0680ba
  else if (thread_option == ATH_THREAD_OPTION_DEFAULT)
Packit 0680ba
    return 0; /* No thread support requested.  */
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
  else
Packit 0680ba
    return 0; /* It is always enabled.  */
Packit 0680ba
#endif /*HAVE_W32_SYSTEM*/
Packit 0680ba
Packit 0680ba
  return GPG_ERR_NOT_SUPPORTED;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Initialize a new mutex.  This function returns 0 on success or an
Packit 0680ba
   system error code (i.e. an ERRNO value).  ERRNO may or may not be
Packit 0680ba
   changed on error.  */
Packit 0680ba
int
Packit 0680ba
ath_mutex_init (ath_mutex_t *lock)
Packit 0680ba
{
Packit 0680ba
  int err;
Packit 0680ba
Packit 0680ba
  switch (thread_model)
Packit 0680ba
    {
Packit 0680ba
    case ath_model_none:
Packit 0680ba
      *lock = MUTEX_UNLOCKED;
Packit 0680ba
      err = 0;
Packit 0680ba
      break;
Packit 0680ba
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
    case ath_model_pthreads:
Packit 0680ba
    case ath_model_pthreads_weak:
Packit 0680ba
      {
Packit 0680ba
        pthread_mutex_t *plck;
Packit 0680ba
Packit 0680ba
        plck = malloc (sizeof *plck);
Packit 0680ba
        if (!plck)
Packit 0680ba
          err = errno? errno : ENOMEM;
Packit 0680ba
        else
Packit 0680ba
          {
Packit 0680ba
            err = pthread_mutex_init (plck, NULL);
Packit 0680ba
            if (err)
Packit 0680ba
              free (plck);
Packit 0680ba
            else
Packit 0680ba
              *lock = (void*)plck;
Packit 0680ba
          }
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*USE_POSIX_THREADS*/
Packit 0680ba
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
    case ath_model_w32:
Packit 0680ba
      {
Packit 0680ba
        CRITICAL_SECTION *csec;
Packit 0680ba
Packit 0680ba
        csec = malloc (sizeof *csec);
Packit 0680ba
        if (!csec)
Packit 0680ba
          err = errno? errno : ENOMEM;
Packit 0680ba
        else
Packit 0680ba
          {
Packit 0680ba
            InitializeCriticalSection (csec);
Packit 0680ba
            *lock = (void*)csec;
Packit 0680ba
            err = 0;
Packit 0680ba
          }
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*HAVE_W32_SYSTEM*/
Packit 0680ba
Packit 0680ba
    default:
Packit 0680ba
      err = EINVAL;
Packit 0680ba
      break;
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  return err;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Destroy a mutex.  This function is a NOP if LOCK is NULL.  If the
Packit 0680ba
   mutex is still locked it can't be destroyed and the function
Packit 0680ba
   returns EBUSY.  ERRNO may or may not be changed on error.  */
Packit 0680ba
int
Packit 0680ba
ath_mutex_destroy (ath_mutex_t *lock)
Packit 0680ba
{
Packit 0680ba
  int err;
Packit 0680ba
Packit 0680ba
  if (!*lock)
Packit 0680ba
    return 0;
Packit 0680ba
Packit 0680ba
  switch (thread_model)
Packit 0680ba
    {
Packit 0680ba
    case ath_model_none:
Packit 0680ba
      if (*lock != MUTEX_UNLOCKED)
Packit 0680ba
        err = EBUSY;
Packit 0680ba
      else
Packit 0680ba
        {
Packit 0680ba
          *lock = MUTEX_DESTROYED;
Packit 0680ba
          err = 0;
Packit 0680ba
        }
Packit 0680ba
      break;
Packit 0680ba
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
    case ath_model_pthreads:
Packit 0680ba
    case ath_model_pthreads_weak:
Packit 0680ba
      {
Packit 0680ba
        pthread_mutex_t *plck = (pthread_mutex_t*) (*lock);
Packit 0680ba
Packit 0680ba
        err = pthread_mutex_destroy (plck);
Packit 0680ba
        if (!err)
Packit 0680ba
          {
Packit 0680ba
            free (plck);
Packit 0680ba
            lock = NULL;
Packit 0680ba
          }
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*USE_POSIX_THREADS*/
Packit 0680ba
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
    case ath_model_w32:
Packit 0680ba
      {
Packit 0680ba
        CRITICAL_SECTION *csec = (CRITICAL_SECTION *)(*lock);
Packit 0680ba
Packit 0680ba
        DeleteCriticalSection (csec);
Packit 0680ba
        free (csec);
Packit 0680ba
        err = 0;
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*HAVE_W32_SYSTEM*/
Packit 0680ba
Packit 0680ba
    default:
Packit 0680ba
      err = EINVAL;
Packit 0680ba
      break;
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  return err;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Lock the mutex LOCK.  On success the function returns 0; on error
Packit 0680ba
   an error code.  ERRNO may or may not be changed on error.  */
Packit 0680ba
int
Packit 0680ba
ath_mutex_lock (ath_mutex_t *lock)
Packit 0680ba
{
Packit 0680ba
  int err;
Packit 0680ba
Packit 0680ba
  switch (thread_model)
Packit 0680ba
    {
Packit 0680ba
    case ath_model_none:
Packit 0680ba
      if (*lock == MUTEX_NOTINIT)
Packit 0680ba
	err = EINVAL;
Packit 0680ba
      else if (*lock == MUTEX_UNLOCKED)
Packit 0680ba
        {
Packit 0680ba
          *lock = MUTEX_LOCKED;
Packit 0680ba
          err = 0;
Packit 0680ba
        }
Packit 0680ba
      else
Packit 0680ba
        err = EDEADLK;
Packit 0680ba
      break;
Packit 0680ba
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
    case ath_model_pthreads:
Packit 0680ba
    case ath_model_pthreads_weak:
Packit 0680ba
      err = pthread_mutex_lock ((pthread_mutex_t*)(*lock));
Packit 0680ba
      break;
Packit 0680ba
#endif /*USE_POSIX_THREADS*/
Packit 0680ba
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
    case ath_model_w32:
Packit 0680ba
      {
Packit 0680ba
        CRITICAL_SECTION *csec = (CRITICAL_SECTION *)(*lock);
Packit 0680ba
Packit 0680ba
        EnterCriticalSection (csec);
Packit 0680ba
        err = 0;
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*HAVE_W32_SYSTEM*/
Packit 0680ba
Packit 0680ba
    default:
Packit 0680ba
      err = EINVAL;
Packit 0680ba
      break;
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  return err;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
/* Unlock the mutex LOCK.  On success the function returns 0; on error
Packit 0680ba
   an error code.  ERRNO may or may not be changed on error.  */
Packit 0680ba
int
Packit 0680ba
ath_mutex_unlock (ath_mutex_t *lock)
Packit 0680ba
{
Packit 0680ba
  int err;
Packit 0680ba
Packit 0680ba
  switch (thread_model)
Packit 0680ba
    {
Packit 0680ba
    case ath_model_none:
Packit 0680ba
      if (*lock == MUTEX_NOTINIT)
Packit 0680ba
	err = EINVAL;
Packit 0680ba
      else if (*lock == MUTEX_LOCKED)
Packit 0680ba
        {
Packit 0680ba
          *lock = MUTEX_UNLOCKED;
Packit 0680ba
          err = 0;
Packit 0680ba
        }
Packit 0680ba
      else
Packit 0680ba
        err = EPERM;
Packit 0680ba
      break;
Packit 0680ba
Packit 0680ba
#if USE_POSIX_THREADS
Packit 0680ba
    case ath_model_pthreads:
Packit 0680ba
    case ath_model_pthreads_weak:
Packit 0680ba
      err = pthread_mutex_unlock ((pthread_mutex_t*)(*lock));
Packit 0680ba
      break;
Packit 0680ba
#endif /*USE_POSIX_THREADS*/
Packit 0680ba
Packit 0680ba
#if HAVE_W32_SYSTEM
Packit 0680ba
    case ath_model_w32:
Packit 0680ba
      {
Packit 0680ba
        CRITICAL_SECTION *csec = (CRITICAL_SECTION *)(*lock);
Packit 0680ba
Packit 0680ba
        LeaveCriticalSection (csec);
Packit 0680ba
        err = 0;
Packit 0680ba
      }
Packit 0680ba
      break;
Packit 0680ba
#endif /*HAVE_W32_SYSTEM*/
Packit 0680ba
Packit 0680ba
    default:
Packit 0680ba
      err = EINVAL;
Packit 0680ba
      break;
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  return err;
Packit 0680ba
}