/** * util/locks.h - unbound locking primitives * * Copyright (c) 2007, NLnet Labs. All rights reserved. * * This software is open source. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the NLNET LABS nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTIL_LOCKS_H #define UTIL_LOCKS_H /** * \file * Locking primitives. * If pthreads is available, these are used. * If no locking exists, they do nothing. * * The idea is to have different sorts of locks for different tasks. * This allows the locking code to be ported more easily. * * Types of locks that are supported. * o lock_rw: lock that has many readers and one writer (to a data entry). * o lock_basic: simple mutex. Blocking, one person has access only. * This lock is meant for non performance sensitive uses. * o lock_quick: speed lock. For performance sensitive locking of critical * sections. Could be implemented by a mutex or a spinlock. * * Also thread creation and deletion functions are defined here. */ #include "util/log.h" /** * The following macro is used to check the return value of the * pthread calls. They return 0 on success and an errno on error. * The errno is logged to the logfile with a descriptive comment. */ #define LOCKRET(func) do {\ int lockret_err; \ if( (lockret_err=(func)) != 0) \ log_err("%s at %d could not " #func ": %s", \ __FILE__, __LINE__, strerror(lockret_err)); \ } while(0) /** DEBUG: use thread debug whenever possible */ #if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_SPINLOCK_T) && defined(ENABLE_LOCK_CHECKS) # define USE_THREAD_DEBUG #endif #ifdef USE_THREAD_DEBUG /******************* THREAD DEBUG ************************/ /* (some) checking; to detect races and deadlocks. */ #include "testcode/checklocks.h" #else /* USE_THREAD_DEBUG */ #define lock_protect(lock, area, size) /* nop */ #define lock_unprotect(lock, area) /* nop */ #define lock_get_mem(lock) (0) /* nothing */ #define checklock_start() /* nop */ #define checklock_stop() /* nop */ #ifdef HAVE_PTHREAD #include /******************* PTHREAD ************************/ /** use pthread mutex for basic lock */ typedef pthread_mutex_t lock_basic_t; /** small front for pthread init func, NULL is default attrs. */ #define lock_basic_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_basic_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) #define lock_basic_lock(lock) LOCKRET(pthread_mutex_lock(lock)) #define lock_basic_unlock(lock) LOCKRET(pthread_mutex_unlock(lock)) #ifndef HAVE_PTHREAD_RWLOCK_T /** in case rwlocks are not supported, use a mutex. */ typedef pthread_mutex_t lock_rw_t; #define lock_rw_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_rw_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) #define lock_rw_rdlock(lock) LOCKRET(pthread_mutex_lock(lock)) #define lock_rw_wrlock(lock) LOCKRET(pthread_mutex_lock(lock)) #define lock_rw_unlock(lock) LOCKRET(pthread_mutex_unlock(lock)) #else /* HAVE_PTHREAD_RWLOCK_T */ /** we use the pthread rwlock */ typedef pthread_rwlock_t lock_rw_t; /** small front for pthread init func, NULL is default attrs. */ #define lock_rw_init(lock) LOCKRET(pthread_rwlock_init(lock, NULL)) #define lock_rw_destroy(lock) LOCKRET(pthread_rwlock_destroy(lock)) #define lock_rw_rdlock(lock) LOCKRET(pthread_rwlock_rdlock(lock)) #define lock_rw_wrlock(lock) LOCKRET(pthread_rwlock_wrlock(lock)) #define lock_rw_unlock(lock) LOCKRET(pthread_rwlock_unlock(lock)) #endif /* HAVE_PTHREAD_RWLOCK_T */ #ifndef HAVE_PTHREAD_SPINLOCK_T /** in case spinlocks are not supported, use a mutex. */ typedef pthread_mutex_t lock_quick_t; /** small front for pthread init func, NULL is default attrs. */ #define lock_quick_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_quick_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) #define lock_quick_lock(lock) LOCKRET(pthread_mutex_lock(lock)) #define lock_quick_unlock(lock) LOCKRET(pthread_mutex_unlock(lock)) #else /* HAVE_PTHREAD_SPINLOCK_T */ /** use pthread spinlock for the quick lock */ typedef pthread_spinlock_t lock_quick_t; /** * allocate process private since this is available whether * Thread Process-Shared Synchronization is supported or not. * This means only threads inside this process may access the lock. * (not threads from another process that shares memory). * spinlocks are not supported on all pthread platforms. */ #define lock_quick_init(lock) LOCKRET(pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE)) #define lock_quick_destroy(lock) LOCKRET(pthread_spin_destroy(lock)) #define lock_quick_lock(lock) LOCKRET(pthread_spin_lock(lock)) #define lock_quick_unlock(lock) LOCKRET(pthread_spin_unlock(lock)) #endif /* HAVE SPINLOCK */ /** Thread creation */ typedef pthread_t ub_thread_t; /** Pass where to store tread_t in thr. Use default NULL attributes. */ #define ub_thread_create(thr, func, arg) LOCKRET(pthread_create(thr, NULL, func, arg)) /** get self id. */ #define ub_thread_self() pthread_self() /** wait for another thread to terminate */ #define ub_thread_join(thread) LOCKRET(pthread_join(thread, NULL)) typedef pthread_key_t ub_thread_key_t; #define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f)) #define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v)) #define ub_thread_key_get(key) pthread_getspecific(key) #else /* we do not HAVE_PTHREAD */ #ifdef HAVE_SOLARIS_THREADS /******************* SOLARIS THREADS ************************/ #include #include typedef rwlock_t lock_rw_t; #define lock_rw_init(lock) LOCKRET(rwlock_init(lock, USYNC_THREAD, NULL)) #define lock_rw_destroy(lock) LOCKRET(rwlock_destroy(lock)) #define lock_rw_rdlock(lock) LOCKRET(rw_rdlock(lock)) #define lock_rw_wrlock(lock) LOCKRET(rw_wrlock(lock)) #define lock_rw_unlock(lock) LOCKRET(rw_unlock(lock)) /** use basic mutex */ typedef mutex_t lock_basic_t; #define lock_basic_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL)) #define lock_basic_destroy(lock) LOCKRET(mutex_destroy(lock)) #define lock_basic_lock(lock) LOCKRET(mutex_lock(lock)) #define lock_basic_unlock(lock) LOCKRET(mutex_unlock(lock)) /** No spinlocks in solaris threads API. Use a mutex. */ typedef mutex_t lock_quick_t; #define lock_quick_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL)) #define lock_quick_destroy(lock) LOCKRET(mutex_destroy(lock)) #define lock_quick_lock(lock) LOCKRET(mutex_lock(lock)) #define lock_quick_unlock(lock) LOCKRET(mutex_unlock(lock)) /** Thread creation, create a default thread. */ typedef thread_t ub_thread_t; #define ub_thread_create(thr, func, arg) LOCKRET(thr_create(NULL, NULL, func, arg, NULL, thr)) #define ub_thread_self() thr_self() #define ub_thread_join(thread) LOCKRET(thr_join(thread, NULL, NULL)) typedef thread_key_t ub_thread_key_t; #define ub_thread_key_create(key, f) LOCKRET(thr_keycreate(key, f)) #define ub_thread_key_set(key, v) LOCKRET(thr_setspecific(key, v)) void* ub_thread_key_get(ub_thread_key_t key); #else /* we do not HAVE_SOLARIS_THREADS and no PTHREADS */ /******************* WINDOWS THREADS ************************/ #ifdef HAVE_WINDOWS_THREADS #include /* Use a mutex */ typedef LONG lock_rw_t; #define lock_rw_init(lock) lock_basic_init(lock) #define lock_rw_destroy(lock) lock_basic_destroy(lock) #define lock_rw_rdlock(lock) lock_basic_lock(lock) #define lock_rw_wrlock(lock) lock_basic_lock(lock) #define lock_rw_unlock(lock) lock_basic_unlock(lock) /** the basic lock is a mutex, implemented opaquely, for error handling. */ typedef LONG lock_basic_t; void lock_basic_init(lock_basic_t* lock); void lock_basic_destroy(lock_basic_t* lock); void lock_basic_lock(lock_basic_t* lock); void lock_basic_unlock(lock_basic_t* lock); /** on windows no spinlock, use mutex too. */ typedef LONG lock_quick_t; #define lock_quick_init(lock) lock_basic_init(lock) #define lock_quick_destroy(lock) lock_basic_destroy(lock) #define lock_quick_lock(lock) lock_basic_lock(lock) #define lock_quick_unlock(lock) lock_basic_unlock(lock) /** Thread creation, create a default thread. */ typedef HANDLE ub_thread_t; void ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg); ub_thread_t ub_thread_self(void); void ub_thread_join(ub_thread_t thr); typedef DWORD ub_thread_key_t; void ub_thread_key_create(ub_thread_key_t* key, void* f); void ub_thread_key_set(ub_thread_key_t key, void* v); void* ub_thread_key_get(ub_thread_key_t key); #else /* we do not HAVE_SOLARIS_THREADS, PTHREADS or WINDOWS_THREADS */ /******************* NO THREADS ************************/ #define THREADS_DISABLED 1 /** In case there is no thread support, define locks to do nothing */ typedef int lock_rw_t; #define lock_rw_init(lock) /* nop */ #define lock_rw_destroy(lock) /* nop */ #define lock_rw_rdlock(lock) /* nop */ #define lock_rw_wrlock(lock) /* nop */ #define lock_rw_unlock(lock) /* nop */ /** define locks to do nothing */ typedef int lock_basic_t; #define lock_basic_init(lock) /* nop */ #define lock_basic_destroy(lock) /* nop */ #define lock_basic_lock(lock) /* nop */ #define lock_basic_unlock(lock) /* nop */ /** define locks to do nothing */ typedef int lock_quick_t; #define lock_quick_init(lock) /* nop */ #define lock_quick_destroy(lock) /* nop */ #define lock_quick_lock(lock) /* nop */ #define lock_quick_unlock(lock) /* nop */ /** Thread creation, threads do not exist */ typedef pid_t ub_thread_t; /** ub_thread_create is simulated with fork (extremely heavy threads, * with no shared memory). */ #define ub_thread_create(thr, func, arg) \ ub_thr_fork_create(thr, func, arg) #define ub_thread_self() getpid() #define ub_thread_join(thread) ub_thr_fork_wait(thread) void ub_thr_fork_wait(ub_thread_t thread); void ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg); typedef void* ub_thread_key_t; #define ub_thread_key_create(key, f) (*(key)) = NULL #define ub_thread_key_set(key, v) (key) = (v) #define ub_thread_key_get(key) (key) #endif /* HAVE_WINDOWS_THREADS */ #endif /* HAVE_SOLARIS_THREADS */ #endif /* HAVE_PTHREAD */ #endif /* USE_THREAD_DEBUG */ /** * Block all signals for this thread. * fatal exit on error. */ void ub_thread_blocksigs(void); /** * unblock one signal for this thread. */ void ub_thread_sig_unblock(int sig); #endif /* UTIL_LOCKS_H */