michael@0: /* michael@0: * Copyright 2009-2012 Niels Provos and Nick Mathewson michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. The name of the author may not be used to endorse or promote products michael@0: * derived from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR michael@0: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES michael@0: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@0: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT michael@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF michael@0: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: #include "event2/event-config.h" michael@0: michael@0: /* With glibc we need to define this to get PTHREAD_MUTEX_RECURSIVE. */ michael@0: #define _GNU_SOURCE michael@0: #include michael@0: michael@0: struct event_base; michael@0: #include "event2/thread.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "mm-internal.h" michael@0: #include "evthread-internal.h" michael@0: michael@0: static pthread_mutexattr_t attr_recursive; michael@0: michael@0: static void * michael@0: evthread_posix_lock_alloc(unsigned locktype) michael@0: { michael@0: pthread_mutexattr_t *attr = NULL; michael@0: pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t)); michael@0: if (!lock) michael@0: return NULL; michael@0: if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE) michael@0: attr = &attr_recursive; michael@0: if (pthread_mutex_init(lock, attr)) { michael@0: mm_free(lock); michael@0: return NULL; michael@0: } michael@0: return lock; michael@0: } michael@0: michael@0: static void michael@0: evthread_posix_lock_free(void *_lock, unsigned locktype) michael@0: { michael@0: pthread_mutex_t *lock = _lock; michael@0: pthread_mutex_destroy(lock); michael@0: mm_free(lock); michael@0: } michael@0: michael@0: static int michael@0: evthread_posix_lock(unsigned mode, void *_lock) michael@0: { michael@0: pthread_mutex_t *lock = _lock; michael@0: if (mode & EVTHREAD_TRY) michael@0: return pthread_mutex_trylock(lock); michael@0: else michael@0: return pthread_mutex_lock(lock); michael@0: } michael@0: michael@0: static int michael@0: evthread_posix_unlock(unsigned mode, void *_lock) michael@0: { michael@0: pthread_mutex_t *lock = _lock; michael@0: return pthread_mutex_unlock(lock); michael@0: } michael@0: michael@0: static unsigned long michael@0: evthread_posix_get_id(void) michael@0: { michael@0: union { michael@0: pthread_t thr; michael@0: #if _EVENT_SIZEOF_PTHREAD_T > _EVENT_SIZEOF_LONG michael@0: ev_uint64_t id; michael@0: #else michael@0: unsigned long id; michael@0: #endif michael@0: } r; michael@0: #if _EVENT_SIZEOF_PTHREAD_T < _EVENT_SIZEOF_LONG michael@0: memset(&r, 0, sizeof(r)); michael@0: #endif michael@0: r.thr = pthread_self(); michael@0: return (unsigned long)r.id; michael@0: } michael@0: michael@0: static void * michael@0: evthread_posix_cond_alloc(unsigned condflags) michael@0: { michael@0: pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t)); michael@0: if (!cond) michael@0: return NULL; michael@0: if (pthread_cond_init(cond, NULL)) { michael@0: mm_free(cond); michael@0: return NULL; michael@0: } michael@0: return cond; michael@0: } michael@0: michael@0: static void michael@0: evthread_posix_cond_free(void *_cond) michael@0: { michael@0: pthread_cond_t *cond = _cond; michael@0: pthread_cond_destroy(cond); michael@0: mm_free(cond); michael@0: } michael@0: michael@0: static int michael@0: evthread_posix_cond_signal(void *_cond, int broadcast) michael@0: { michael@0: pthread_cond_t *cond = _cond; michael@0: int r; michael@0: if (broadcast) michael@0: r = pthread_cond_broadcast(cond); michael@0: else michael@0: r = pthread_cond_signal(cond); michael@0: return r ? -1 : 0; michael@0: } michael@0: michael@0: static int michael@0: evthread_posix_cond_wait(void *_cond, void *_lock, const struct timeval *tv) michael@0: { michael@0: int r; michael@0: pthread_cond_t *cond = _cond; michael@0: pthread_mutex_t *lock = _lock; michael@0: michael@0: if (tv) { michael@0: struct timeval now, abstime; michael@0: struct timespec ts; michael@0: evutil_gettimeofday(&now, NULL); michael@0: evutil_timeradd(&now, tv, &abstime); michael@0: ts.tv_sec = abstime.tv_sec; michael@0: ts.tv_nsec = abstime.tv_usec*1000; michael@0: r = pthread_cond_timedwait(cond, lock, &ts); michael@0: if (r == ETIMEDOUT) michael@0: return 1; michael@0: else if (r) michael@0: return -1; michael@0: else michael@0: return 0; michael@0: } else { michael@0: r = pthread_cond_wait(cond, lock); michael@0: return r ? -1 : 0; michael@0: } michael@0: } michael@0: michael@0: int michael@0: evthread_use_pthreads(void) michael@0: { michael@0: struct evthread_lock_callbacks cbs = { michael@0: EVTHREAD_LOCK_API_VERSION, michael@0: EVTHREAD_LOCKTYPE_RECURSIVE, michael@0: evthread_posix_lock_alloc, michael@0: evthread_posix_lock_free, michael@0: evthread_posix_lock, michael@0: evthread_posix_unlock michael@0: }; michael@0: struct evthread_condition_callbacks cond_cbs = { michael@0: EVTHREAD_CONDITION_API_VERSION, michael@0: evthread_posix_cond_alloc, michael@0: evthread_posix_cond_free, michael@0: evthread_posix_cond_signal, michael@0: evthread_posix_cond_wait michael@0: }; michael@0: /* Set ourselves up to get recursive locks. */ michael@0: if (pthread_mutexattr_init(&attr_recursive)) michael@0: return -1; michael@0: if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)) michael@0: return -1; michael@0: michael@0: evthread_set_lock_callbacks(&cbs); michael@0: evthread_set_condition_callbacks(&cond_cbs); michael@0: evthread_set_id_callback(evthread_posix_get_id); michael@0: return 0; michael@0: }