/*
* 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 <pthread.h>
#else
# error Missing <pthread.h>
#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 */
syntax highlighted by Code2HTML, v. 0.9.1