/* posix-lock.c - GPGRT lock functions for POSIX systems Copyright (C) 2005-2009 Free Software Foundation, Inc. Copyright (C) 2014 g10 Code GmbH This file is part of libgpg-error. libgpg-error 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. libgpg-error 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . Parts of the code, in particular use_pthreads_p, are based on code from gettext, written by Bruno Haible , 2005. */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_W32_SYSTEM # error This module may not be build for Windows. #endif #include #include #include #include #if USE_POSIX_THREADS # include #endif #include "gpg-error.h" #include "lock.h" #include "posix-lock-obj.h" #if USE_POSIX_THREADS # if USE_POSIX_THREADS_WEAK /* On ELF systems it is easy to use pthreads using weak references. Take care not to test the address of a weak referenced function we actually use; some GCC versions have a bug were &foo != NULL is always evaluated to true in PIC mode. */ # pragma weak pthread_cancel # pragma weak pthread_mutex_init # pragma weak pthread_mutex_lock # pragma weak pthread_mutex_unlock # pragma weak pthread_mutex_destroy # if ! PTHREAD_IN_USE_DETECTION_HARD # define use_pthread_p() (!!pthread_cancel) # endif # else /*!USE_POSIX_THREADS_WEAK*/ # if ! PTHREAD_IN_USE_DETECTION_HARD # define use_pthread_p() (1) # endif # endif /*!USE_POSIX_THREADS_WEAK*/ # if PTHREAD_IN_USE_DETECTION_HARD /* The function to be executed by a dummy thread. */ static void * dummy_thread_func (void *arg) { return arg; } static int use_pthread_p (void) { static int tested; static int result; /* 1: linked with -lpthread, 0: only with libc */ if (!tested) { pthread_t thread; if (pthread_create (&thread, NULL, dummy_thread_func, NULL)) result = 0; /* Thread creation failed. */ else { /* Thread creation works. */ void *retval; if (pthread_join (thread, &retval) != 0) abort (); result = 1; } tested = 1; } return result; } #endif /*PTHREAD_IN_USE_DETECTION_HARD*/ #endif /*USE_POSIX_THREADS*/ static _gpgrt_lock_t * get_lock_object (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd; if (lock->vers != LOCK_ABI_VERSION) abort (); if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t)) abort (); return lock; } gpg_err_code_t gpgrt_lock_init (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = get_lock_object (lockhd); int rc; #if USE_POSIX_THREADS if (use_pthread_p()) { rc = pthread_mutex_init (&lock->mtx, NULL); if (rc) rc = gpg_err_code_from_errno (rc); } else rc = 0; /* Threads are not used. */ #else /* Unknown thread system. */ rc = GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; } gpg_err_code_t gpgrt_lock_lock (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = get_lock_object (lockhd); int rc; #if USE_POSIX_THREADS if (use_pthread_p()) { rc = pthread_mutex_lock (&lock->mtx); if (rc) rc = gpg_err_code_from_errno (rc); } else rc = 0; /* Threads are not used. */ #else /* Unknown thread system. */ rc = GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; } gpg_err_code_t gpgrt_lock_unlock (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = get_lock_object (lockhd); int rc; #if USE_POSIX_THREADS if (use_pthread_p()) { rc = pthread_mutex_unlock (&lock->mtx); if (rc) rc = gpg_err_code_from_errno (rc); } else rc = 0; /* Threads are not used. */ #else /* Unknown thread system. */ rc = GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; } /* Note: Use this function only if no other thread holds or waits for this lock. */ gpg_err_code_t gpgrt_lock_destroy (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = get_lock_object (lockhd); int rc; #if USE_POSIX_THREADS if (use_pthread_p()) { rc = pthread_mutex_destroy (&lock->mtx); if (rc) rc = gpg_err_code_from_errno (rc); else { /* Re-init the the mutex so that it can be re-used. */ gpgrt_lock_t tmp = GPGRT_LOCK_INITIALIZER; memcpy (lockhd, &tmp, sizeof tmp); } } else rc = 0; /* Threads are not used. */ #else /* Unknown thread system. */ rc = GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; }