/* ath.c - Thread-safeness library. Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of Libgcrypt. Libgcrypt 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. Libgcrypt 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 General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Libgcrypt; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include /* Right: We need to use assert and not gcry_assert. */ #include #ifdef HAVE_SYS_SELECT_H # include #else # include #endif #include #ifndef _WIN32 #include #endif #include #include "ath.h" /* The interface table. */ static struct ath_ops ops; /* True if we should use the external callbacks. */ static int ops_set; /* For the dummy interface. */ #define MUTEX_UNLOCKED ((ath_mutex_t) 0) #define MUTEX_LOCKED ((ath_mutex_t) 1) #define MUTEX_DESTROYED ((ath_mutex_t) 2) /* Return the thread type from the option field. */ #define GET_OPTION(a) ((a) & 0xff) /* Return the version number from the option field. */ #define GET_VERSION(a) (((a) >> 8)& 0xff) /* The lock we take while checking for lazy lock initialization. */ static ath_mutex_t check_init_lock = ATH_MUTEX_INITIALIZER; int ath_init (void) { int err = 0; if (ops_set) { if (ops.init) err = (*ops.init) (); if (err) return err; err = (*ops.mutex_init) (&check_init_lock); } return err; } /* Initialize the locking library. Returns 0 if the operation was successful, EINVAL if the operation table was invalid and EBUSY if we already were initialized. */ gpg_err_code_t ath_install (struct ath_ops *ath_ops, int check_only) { if (check_only) { unsigned int option = 0; /* Check if the requested thread option is compatible to the thread option we are already committed to. */ if (ath_ops) option = ath_ops->option; if (!ops_set && GET_OPTION (option)) return GPG_ERR_NOT_SUPPORTED; if (GET_OPTION (ops.option) == ATH_THREAD_OPTION_USER || GET_OPTION (option) == ATH_THREAD_OPTION_USER || GET_OPTION (ops.option) != GET_OPTION (option) || GET_VERSION (ops.option) != GET_VERSION (option)) return GPG_ERR_NOT_SUPPORTED; return 0; } if (ath_ops) { /* It is convenient to not require DESTROY. */ if (!ath_ops->mutex_init || !ath_ops->mutex_lock || !ath_ops->mutex_unlock) return GPG_ERR_INV_ARG; ops = *ath_ops; ops_set = 1; } else ops_set = 0; return 0; } static int mutex_init (ath_mutex_t *lock, int just_check) { int err = 0; if (just_check) (*ops.mutex_lock) (&check_init_lock); if (*lock == ATH_MUTEX_INITIALIZER || !just_check) err = (*ops.mutex_init) (lock); if (just_check) (*ops.mutex_unlock) (&check_init_lock); return err; } int ath_mutex_init (ath_mutex_t *lock) { if (ops_set) return mutex_init (lock, 0); #ifndef NDEBUG *lock = MUTEX_UNLOCKED; #endif return 0; } int ath_mutex_destroy (ath_mutex_t *lock) { if (ops_set) { if (!ops.mutex_destroy) return 0; (*ops.mutex_lock) (&check_init_lock); if (*lock == ATH_MUTEX_INITIALIZER) { (*ops.mutex_unlock) (&check_init_lock); return 0; } (*ops.mutex_unlock) (&check_init_lock); return (*ops.mutex_destroy) (lock); } #ifndef NDEBUG assert (*lock == MUTEX_UNLOCKED); *lock = MUTEX_DESTROYED; #endif return 0; } int ath_mutex_lock (ath_mutex_t *lock) { if (ops_set) { int ret = mutex_init (lock, 1); if (ret) return ret; return (*ops.mutex_lock) (lock); } #ifndef NDEBUG assert (*lock == MUTEX_UNLOCKED); *lock = MUTEX_LOCKED; #endif return 0; } int ath_mutex_unlock (ath_mutex_t *lock) { if (ops_set) { int ret = mutex_init (lock, 1); if (ret) return ret; return (*ops.mutex_unlock) (lock); } #ifndef NDEBUG assert (*lock == MUTEX_LOCKED); *lock = MUTEX_UNLOCKED; #endif return 0; } ssize_t ath_read (int fd, void *buf, size_t nbytes) { if (ops_set && ops.read) return (*ops.read) (fd, buf, nbytes); else return _read (fd, buf, nbytes); } ssize_t ath_write (int fd, const void *buf, size_t nbytes) { if (ops_set && ops.write) return (*ops.write) (fd, buf, nbytes); else return _write (fd, buf, nbytes); } ssize_t #ifdef _WIN32 ath_select (int nfd, void *rset, void *wset, void *eset, struct timeval *timeout) #else ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset, struct timeval *timeout) #endif { if (ops_set && ops.select) return (*ops.select) (nfd, rset, wset, eset, timeout); else #ifdef _WIN32 return -1; #else return select (nfd, rset, wset, eset, timeout); #endif } ssize_t ath_waitpid (pid_t pid, int *status, int options) { if (ops_set && ops.waitpid) return (*ops.waitpid) (pid, status, options); else #ifdef _WIN32 return -1; #else return waitpid (pid, status, options); #endif } int #ifdef _WIN32 ath_accept (int s, void *addr, int *length_ptr) #else ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr) #endif { if (ops_set && ops.accept) return (*ops.accept) (s, addr, length_ptr); else #ifdef _WIN32 return -1; #else return accept (s, addr, length_ptr); #endif } int #ifdef _WIN32 ath_connect (int s, void *addr, int length) #else ath_connect (int s, struct sockaddr *addr, socklen_t length) #endif { if (ops_set && ops.connect) return (*ops.connect) (s, addr, length); else #ifdef _WIN32 return -1; #else return connect (s, addr, length); #endif } int #ifdef _WIN32 ath_sendmsg (int s, const void *msg, int flags) #else ath_sendmsg (int s, const struct msghdr *msg, int flags) #endif { if (ops_set && ops.sendmsg) return (*ops.sendmsg) (s, msg, flags); else #ifdef _WIN32 return -1; #else return sendmsg (s, msg, flags); #endif } int #ifdef _WIN32 ath_recvmsg (int s, void *msg, int flags) #else ath_recvmsg (int s, struct msghdr *msg, int flags) #endif { if (ops_set && ops.recvmsg) return (*ops.recvmsg) (s, msg, flags); else #ifdef _WIN32 return -1; #else return recvmsg (s, msg, flags); #endif }