/* * Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * $Id: pthread.h,v 1.20 2006/03/14 19:35:00 ca Exp $ */ #ifndef SM_PTHREAD_H #define SM_PTHREAD_H 1 #if HAVE_PTHREAD_H # include #else # error Missing #endif #include "sm/error.h" #include "sm/assert.h" #ifndef SM_MUTEX_DEBUG # define SM_MUTEX_DEBUG 0 #endif #ifndef SM_LOCK_DEBUG # define SM_LOCK_DEBUG 0 #endif sm_ret_T thr_init(void); sm_ret_T thr_stop(void); /* default attribute for pthread_mutex_init() */ #define SM_PTHREAD_MUTEXATTR NULL #if SM_MUTEX_DEBUG /* ** Debugging versions of mutex_(un)lock() ** These require that the macro SMFCT is set to the function name. ** The functions simply print to smioerr, which is good enough for debugging. */ int sm_thread_mutex_lock(pthread_mutex_t *mutex, const char *mname, const char *fct); int sm_thread_mutex_unlock(pthread_mutex_t *mutex, const char *mname, const char *fct); #define smthread_mutex_lock(mutex) sm_thread_mutex_lock(mutex, #mutex, SMFCT) #define smthread_mutex_unlock(mutex) sm_thread_mutex_unlock(mutex, #mutex, SMFCT) #else /* SM_MUTEX_DEBUG */ #define smthread_mutex_lock(mutex) pthread_mutex_lock(mutex) #define smthread_mutex_unlock(mutex) pthread_mutex_unlock(mutex) #endif /* SM_MUTEX_DEBUG */ /* ** Require that a pthread_mutex_lock() worked. ** This is its own macro in case we don't want to abort on an error. */ #define SM_LOCK_OK(r) SM_ASSERT((r) == 0) /* Require that a mutex is locked (useful for debugging) */ #if SM_LOCK_DEBUG #define SM_IS_LOCKED(mutex) \ do \ { \ int r; \ r = pthread_mutex_trylock(mutex); \ SM_ASSERT(r == EBUSY); \ } while (0) #else #define SM_IS_LOCKED(mutex) SM_NOOP #endif /* ** Parameters for functions to lock/unlock their data structures: ** nolocking, lock it, unlock it if no error, unlock on error. ** This is better than having a boolean since now we can also return ** a data structure which is still locked (to operate on it, and ** unlock it later). ** ** Behavior: ** LOCK_IT: lock mutex ** UNL_NO_ERR: unlock mutex if no error occurs ** UNL_IF_ERR: unlock mutex on error return ** UNLOCK_IT: always unlock mutex on return ** ** To have a function: ** perform no locking at all: THR_NO_LOCK ** lock/unlock in all cases: THR_LOCK_UNLOCK (normal operation; set all flags) ** lock but only unlock on error: THR_LOCK_UNLERR ** this is useful to have a function lock a mutex and keep it locked ** after return unless an error occurred. ** just lock: THR_LOCK_IT ** lock but only unlock if no error: THR_LOCK_IT|THR_UNL_NO_ERR ** only unlock if no error: THR_UNL_NO_ERR ** only unlock if error: THR_UNL_IF_ERR ** ** A function looks like this: ** if (thr_lock_it(locktype)) ** lock(mutex) ** ... do work ... ** if (thr_unl_no_err(locktype)) ** lock(mutex) ** return OK; ** error: ** if (thr_unl_if_err(locktype)) ** lock(mutex) ** return ERROR; ** ** if there is no error: label: ** if ((!sm_is_err(ret) && thr_unl_no_err(locktype)) ** || (sm_is_err(ret) && thr_unl_if_err(locktype))) ** ** XXX Question: when is thr_unl_always() used? */ typedef uint thr_lock_T; #define THR_NO_LOCK 0x0000u /* no locking */ #define THR_LOCK_IT 0x0001u /* lock it */ #define THR_UNL_NO_ERR 0x0002u /* unlock it if no error */ #define THR_UNL_IF_ERR 0x0004u /* unlock it if error */ #define THR_UNLOCK_IT (THR_UNL_NO_ERR|THR_UNL_IF_ERR) #define THR_LOCK_UNLOCK (THR_LOCK_IT|THR_UNLOCK_IT) #define THR_LOCK_UNLERR (THR_LOCK_IT|THR_UNL_IF_ERR) #define thr_lock_it(lck) (((lck) & THR_LOCK_IT) != 0) #define thr_unl_if_err(lck) (((lck) & THR_UNL_IF_ERR) != 0) #define thr_unl_no_err(lck) (((lck) & THR_UNL_NO_ERR) != 0) #define thr_unl_always(lck) (((lck) & THR_UNLOCK_IT) != 0) /* ** Locking for substructure ** This can be used in the following situation: ** a structure contains a (list/hash/queue/...) of substructures, ** each of which can be locked individually. ** A caller can request to lock the structure and its substructures. ** Example: ** struct s1 {mutex s1m; list_s2 s1l; } ** struct s2 {mutex s2m; item s2i; } ** function: find entry: ** lock s1m, find matching entry s2, lock s2m, unlock s1m ** return s2 (locked) to caller. ** Note: this isn't used yet. */ #define THR_NO_LOCK_S1 0x0000u /* no locking */ #define THR_LOCK_IT_S1 0x0010u /* lock it */ #define THR_UNL_NO_ERR_S1 0x0020u /* unlock it if no error */ #define THR_UNL_IF_ERR_S1 0x0040u /* unlock it if error */ #define THR_UNLOCK_IT_S1 (THR_UNL_NO_ERR_S1|THR_UNL_IF_ERR_S1) #define THR_LOCK_UNLOCK_S1 (THR_LOCK_IT_S1|THR_UNLOCK_IT_S1) #define THR_LOCK_UNLERR_S1 (THR_LOCK_IT_S1|THR_UNL_IF_ERR_S1) #define thr_lock_it_s1(lck) (((lck) & THR_LOCK_IT_S1) != 0) #define thr_unl_if_err_s1(lck) (((lck) & THR_UNL_IF_ERR_S1) != 0) #define thr_unl_no_err_s1(lck) (((lck) & THR_UNL_NO_ERR_S1) != 0) #define thr_unl_always_s1(lck) (((lck) & THR_UNLOCK_IT_S1) != 0) /* ** Locking for second level function ** This can be used in the following situation: ** a function L1 calls another function L2 both of which need locking. ** A caller can request to lock the structures in L1 and L2 independently. ** Note: this isn't used yet. */ #define THR_NO_LOCK_L2 0x0000u /* no locking */ #define THR_LOCK_IT_L2 0x0100u /* lock it */ #define THR_UNL_NO_ERR_L2 0x0200u /* unlock it if no error */ #define THR_UNL_IF_ERR_L2 0x0400u /* unlock it if error */ /* convert level 2 locktype to "normal" locktype */ #define THR_L2_TO_L0(lck) ((lck) >> 8) #define THR_UNLOCK_IT_L2 (THR_UNL_NO_ERR_L2|THR_UNL_IF_ERR_L2) #define THR_LOCK_UNLOCK_L2 (THR_LOCK_IT_L2|THR_UNLOCK_IT_L2) #define THR_LOCK_UNLERR_L2 (THR_LOCK_IT_L2|THR_UNL_IF_ERR_L2) #define thr_lock_it_l2(lck) (((lck) & THR_LOCK_IT_L2) != 0) #define thr_unl_if_err_l2(lck) (((lck) & THR_UNL_IF_ERR_L2) != 0) #define thr_unl_no_err_l2(lck) (((lck) & THR_UNL_NO_ERR_L2) != 0) #define thr_unl_always_l2(lck) (((lck) & THR_UNLOCK_IT_L2) != 0) #if !HAVE_PTHREAD_RWLOCK_INIT /* ** XXX HACK ... provide only single threaded access for those OS ** that do not have pthread_rwlock. */ #define pthread_rwlock_destroy(m) pthread_mutex_destroy(m) #define pthread_rwlock_init(m, a) pthread_mutex_init((m), (a)) #define pthread_rwlock_rdlock(m) pthread_mutex_lock(m) #define pthread_rwlock_wrlock(m) pthread_mutex_lock(m) #define pthread_rwlock_unlock(m) pthread_mutex_unlock(m) #define pthread_rwlock_t pthread_mutex_t #endif /* !HAVE_PTHREAD_RWLOCK_INIT */ #endif /* SM_PTHREAD_H */