/*
* Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 2006 Claus Assmann
*
* 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: evthr.h,v 1.94 2007/01/01 02:10:25 ca Exp $
*/
#ifndef SM_EVTHR_H
#define SM_EVTHR_H 1
#include "sm/generic.h"
#include "sm/magic.h"
#include "sm/types.h"
#include "sm/time.h"
#include "sm/queue.h"
#include "sm/pthread.h"
#include "sm/socket.h"
#include "sm/io.h"
#include "sm/log.h"
#include "sm/note.h"
#ifndef EVTHR_DEBUG
# define EVTHR_DEBUG 0
#endif
/* put some additional checks into place? */
#ifndef EVTHR_PARANOIA
# define EVTHR_PARANOIA 0
#endif
#ifndef SM_EVTHR_CV
/* do NOT activate this; it has not been implemented yet! */
# define SM_EVTHR_CV 0
#endif
#ifndef SM_LOCK_TASK
# define SM_LOCK_TASK 0
#endif
/* number of priorities */
#ifndef EVTHR_PRIOS
# define EVTHR_PRIOS 1
#endif
/* max. signals handled by application: USR1, USR2 */
#define EVTHR_MAX_SIGS 2
/* for debugging */
#define ERRPRINTTV(msg, tv) fprintf(stderr, msg "%ld.%06ld\n", (tv).tv_sec, (tv).tv_usec)
typedef struct sm_evthr_ctx_S sm_evthr_ctx_T, *sm_evthr_ctx_P;
typedef struct sm_evthr_task_S sm_evthr_task_T, *sm_evthr_task_P;
typedef struct sm_evthr_req_S sm_evthr_req_T, *sm_evthr_req_P;
#define SM_IS_EVTHR_CTX(ctx) SM_REQUIRE_ISA(ctx, SM_EVTHR_CTX_MAGIC)
#define SM_IS_EVTHR_TSK(tsk) SM_REQUIRE_ISA(tsk, SM_EVTHR_TASK_MAGIC)
/* use an rpool here?? */
struct sm_evthr_ctx_S
{
sm_magic_T sm_magic;
pthread_cond_t evthr_c_cv;
/* for waitq */
pthread_mutex_t evthr_c_waitqmut;
/* for runq and counters c_cur, c_idl etc */
pthread_mutex_t evthr_c_runqmut;
uint evthr_c_max_h; /* max. # of threads (hard limit) */
uint evthr_c_max_s; /* max. # of threads (soft limit) */
uint evthr_c_min; /* min. number of threads */
uint evthr_c_cur; /* current number of threads */
uint evthr_c_idl; /* idle threads */
uint evthr_c_act; /* active threads */
uint evthr_c_flags; /* flags, see below */
uint evthr_c_wflags;/* flags, see below */
uint evthr_c_maxfd; /* maximum number of FDs */
timeval_T evthr_c_time; /* current time */
sm_evthr_task_P *evthr_c_fd2t; /* array to map FDs to tasks */
/* array to map signals to tasks */
sm_evthr_task_P evthr_c_sg2t[EVTHR_MAX_SIGS];
uint evthr_c_tasks; /* number of tasks */
sm_log_ctx_P evthr_c_lctx;
/* pipe between control thread and worker/signal threads */
/* should this be fd_T?? */
int evthr_c_pipe[2];
/*
** Simple way to let the main loop "know" about errors in
** the signal handler module (which must only use "signal-safe"
** functions).
** A more "sophisticated" way is to use a (fixed sized) queue
** in case more than one error occurs before the main loop
** can react.
*/
int evthr_sige_where;
int evthr_sige_what;
CIRCLEQ_HEAD(, sm_evthr_task_S) evthr_c_waitq;
#if EVTHR_PRIOS <= 1
CIRCLEQ_HEAD(, sm_evthr_task_S) evthr_c_runq;
#else
CIRCLEQ_HEAD(, sm_evthr_task_S) evthr_c_runq[EVTHR_PRIOS];
uint evthr_c_nprio;
#endif
/* change requests: mutex and list of change requests */
pthread_mutex_t evthr_c_reqmut;
uint evthr_c_nreqs;
/* total # of entries in request queue including those that aren't used */
uint evthr_c_tot_reqs;
/* list of requests (free [evthr_r_task==NULL] or in use) */
CIRCLEQ_HEAD(, sm_evthr_req_S) evthr_c_reqs;
#if SM_EVTHR_CV
uint evthr_c_ncvs;
CIRCLEQ_HEAD(, sm_evthr_req_S) evthr_c_cvq;
#endif /* SM_EVTHR_CV */
#if EVTHR_DEBUG
uint evthr_c_dbglvl;
#endif
};
NOTE(LOCK_ORDER(sm_evthr_ctx_S::evthr_c_runqmut sm_evthr_ctx_S::evthr_c_waitqmut))
NOTE(MUTEX_PROTECTS_DATA(sm_evthr_ctx_S::evthr_c_waitqmut, sm_evthr_ctx_S::evthr_c_waitq))
NOTE(MUTEX_PROTECTS_DATA(sm_evthr_ctx_S::evthr_c_runqmut, sm_evthr_ctx_S::evthr_c_runq))
NOTE(MUTEX_PROTECTS_DATA(sm_evthr_ctx_S::evthr_c_reqmut, sm_evthr_ctx_S::evthr_c_reqs))
NOTE(DATA_READABLE_WITHOUT_LOCK(sm_evthr_ctx_S::sm_magic))
#define EVTHR_FL_NONE 0x0000
/* stop all threads; maybe types like shutdown, abort, ...? */
#define EVTHR_FL_STOP 0x0001
/* protected by runq mutex? */
/*
** soft limit exceeded: if this is already set then more workers
** (up to the hard limit) can be started.
** Note: it might be useful to have a counter or a time limit instead
** that must be exceeded (this is just a counter of 1).
*/
#define EVTHR_FL_SL_EXC 0x0010
/* protected by runq mutex? */
#define EVTHR_SET_FLAG(evthr_ctx, fl) (evthr_ctx)->evthr_c_flags |= (fl)
#define EVTHR_CLR_FLAG(evthr_ctx, fl) (evthr_ctx)->evthr_c_flags &= ~(fl)
#define EVTHR_IS_FLAG(evthr_ctx, fl) (((evthr_ctx)->evthr_c_flags & (fl)) != 0)
/* wflags are protected by waitq mutex */
#define EVTHR_WFL_NONE 0x0000
/* scheduler has been informed to "wake up", no need to tell it again */
#define EVTHR_WFL_WAKEUP 0x0001
#define EVTHR_SET_WFLAG(evthr_ctx, fl) (evthr_ctx)->evthr_c_wflags |= (fl)
#define EVTHR_CLR_WFLAG(evthr_ctx, fl) (evthr_ctx)->evthr_c_wflags &= ~(fl)
#define EVTHR_IS_WFLAG(evthr_ctx, fl) (((evthr_ctx)->evthr_c_wflags & (fl)) != 0)
#define EVTHR_SET_WAKEUP(evthr_ctx) EVTHR_SET_WFLAG(evthr_ctx, EVTHR_WFL_WAKEUP)
#define EVTHR_CLR_WAKEUP(evthr_ctx) EVTHR_CLR_WFLAG(evthr_ctx, EVTHR_WFL_WAKEUP)
#define EVTHR_IS_WAKEUP(evthr_ctx) EVTHR_IS_WFLAG(evthr_ctx, EVTHR_WFL_WAKEUP)
/* for listen() sockets: accepted fd */
struct sm_evthr_nc_S
{
socklen_T evthr_a_len;
struct sockaddr evthr_a_addr;
int evthr_a_fd;
};
typedef struct sm_evthr_nc_S sm_evthr_nc_T, *sm_evthr_nc_P;
/*
** requests for change of event flags (task status)
*/
struct sm_evthr_req_S
{
CIRCLEQ_ENTRY(sm_evthr_req_S) evthr_r_next;
sm_evthr_task_P evthr_r_task; /* pointer to task for identification */
int evthr_r_rqevf; /* see below */
timeval_T evthr_r_sleep; /* when to wake up */
int evthr_r_chge; /* when to change wakeup time */
};
/* must be ordered */
#define EVTHR_CHG_TIME_NO 0
#define EVTHR_CHG_TIME_YES 1
#define EVTHR_CHG_TIME_LESS 2
#define EVTHR_REQ_CLR(req) do { \
(req)->evthr_r_task = NULL; \
(req)->evthr_r_rqevf = 0; \
(req)->evthr_r_chge = 0; \
} while (0)
#if SM_EVTHR_CV
NOT YET IMPLEMENTED!
Need to determine first whether this is really required/useful.
/*
** Queue of tasks waiting for a condition (result)
*/
struct sm_evthr_cv_S
{
sm_evthr_task_P evthr_cv_task;
uint evthr_cv_id;
pthread_mutex_t evthr_cv_mutex;
CIRCLEQ_ENTRY(sm_evthr_cv_S) evthr_cv_next;
};
sm_ret_T evthr_cv_get_id(sm_evthr_ctx_P _ctx, uint *pid);
#endif /* SM_EVTHR_CV */
/*
** worker function prototype
** receives task as argument; access the application context through it
*/
typedef sm_ret_T (evthr_task_F)(sm_evthr_task_P);
/*
** Task description
** Do we want to have a mutex per task?
** When do we need it? Only if we manipulate the task itself.
** However, each task is (almost) always in some queue and those
** queues are protected by mutexes.
*/
struct sm_evthr_task_S
{
sm_magic_T sm_magic;
CIRCLEQ_ENTRY(sm_evthr_task_S) evthr_t_next;
#if SM_LOCK_TASK
/* protects evthr_t_rqevf and evthr_t_sleep */
pthread_mutex_t evthr_t_mutex;
#endif
/* types of event on which this tasks waits */
uint32_t evthr_t_rqevf; /* see below */
uint32_t evthr_t_evocc; /* events occurred; see below */
uint32_t evthr_t_state; /* current state; see below */
uint32_t evthr_t_flags; /* flags; see below */
int evthr_t_fd; /* fd to watch */
int evthr_t_sig; /* signal to watch */
timeval_T evthr_t_sleep; /* when to wake up */
evthr_task_F *evthr_t_fct; /* function to execute */
void *evthr_t_actx; /* application context */
sm_evthr_ctx_P evthr_t_ctx; /* evthr context */
sm_evthr_nc_P evthr_t_nc; /* network connection */
#if EVTHR_PRIOS > 1
int evthr_t_prio; /* priority */
# define EVTHR_T_PRIO(task) ((task)->evthr_t_prio)
# define EVTHR_T_PRIO_SET(task, prio) ((task)->evthr_t_prio) = (prio)
#else
# define EVTHR_T_PRIO(task) 0
# define EVTHR_T_PRIO_SET(task, prio) SM_NOOP
#endif
};
/*
** evthr_t_rqevf: types of event on which this task is waiting
** (8 bits max: 0xFF)
*/
#define EVTHR_EV_RD 0x00000001 /* read */
#define EVTHR_EV_WR 0x00000002 /* write */
#define EVTHR_EV_LI 0x00000004 /* listen (accept()) */
#define EVTHR_EV_IOM 0x00000007 /* mask for I/O */
#define EVTHR_EV_SL 0x00000008 /* sleep (timeout) */
#define EVTHR_EV_SG 0x00000010 /* signal */
/*
** evthr_t_evocc: types of event which occurred
** (8 bits max: 0xFF00, currently only 5 bits)
*/
#define EVTHR_EV_RD_Y 0x00000100 /* ready for read */
#define EVTHR_EV_WR_Y 0x00000200 /* ready for write */
#define EVTHR_EV_LI_Y 0x00000400 /* ready for listen (accept()) */
#define EVTHR_EV_SL_Y 0x00000800 /* wakeup (after timeout) */
#define EVTHR_EV_SG_Y 0x00001000 /* signal */
#define EVTHR_EV_RDY 0x00003F00 /* mask for ready (event occurred) */
/* status of task (which queue?) */
#if EVTHR_PARANOIA
# define EVTHR_EV_CHK 0x00000001 /* is in waitq, already checked */
#endif
#define EVTHR_EV_IWQ 0x00010000 /* is in waitq */
#define EVTHR_EV_IRQ 0x00020000 /* is in runq */
#define EVTHR_EV_WWQ 0x00100000 /* wants to be in waitq */
#define EVTHR_EV_WRQ 0x00200000 /* wants to be in runq */
#define EVTHR_EV_DEL 0x00800000 /* wants to be deleted */
#define EVTHR_EV_WALL (EVTHR_EV_WWQ|EVTHR_EV_WRQ|EVTHR_EV_DEL)
#define EVTHR_EV_QALL (EVTHR_EV_WALL|EVTHR_EV_IWQ|EVTHR_EV_IRQ)
#define EVTHR_IS_INQ(task, q) do { \
(task)->evthr_t_state &= ~EVTHR_EV_QALL; \
(task)->evthr_t_state |= (q); \
} while (0)
#define EVTHR_WANTS_INQ(task, q) (task)->evthr_t_state |= (q)
#define EVTHR_REM_FROMQ(task, q) (task)->evthr_t_state &= ~(q)
#if EVTHR_PARANOIA
# define EVTHR_ALREADY_CHK(task) (((task)->evthr_t_state &= EVTHR_EV_CHK) != 0)
# define EVTHR_CHECKED(task) (task)->evthr_t_state |= EVTHR_EV_CHK
# define EVTHR_CLR_CHECKED(task) (task)->evthr_t_state &= ~EVTHR_EV_CHK
#endif /* EVTHR_PARANOIA */
#define EVTHR_GOT_EV(task, ev) (task)->evthr_t_evocc |= (ev)
/* manipulate evthr_t_rqevf */
#define evthr_set_ev(task, ev) (task)->evthr_t_rqevf |= (ev)
#define evthr_clr_ev(task, ev) (task)->evthr_t_rqevf &= ~(ev)
#define evthr_rqevents(task) ((task)->evthr_t_rqevf)
#define evthr_is_ev(task, w) (((task)->evthr_t_rqevf & (w)) != 0)
#define evthr_is_rd(task) evthr_is_ev((task), EVTHR_EV_RD)
#define evthr_is_wr(task) evthr_is_ev((task), EVTHR_EV_WR)
#define evthr_is_li(task) evthr_is_ev((task), EVTHR_EV_LI)
#define evthr_is_io(task) evthr_is_ev((task), EVTHR_EV_IOM)
#define evthr_is_slp(task) evthr_is_ev((task), EVTHR_EV_SL)
#define evthr_is_st(task, w) (((task)->evthr_t_state & (w)) != 0)
#define evthr_is_inq(task, q) evthr_is_st((task), (q))
#define evthr_is_inwq(task) evthr_is_st((task), EVTHR_EV_IWQ)
#define evthr_is_inrq(task) evthr_is_st((task), EVTHR_EV_IRQ)
#define evthr_ev_occ(task, w) (((task)->evthr_t_evocc & (w)) != 0)
#define evthr_clr_rdy(task) (task)->evthr_t_evocc &= ~EVTHR_EV_RDY;
#define evthr_got_rd(task) evthr_ev_occ((task), EVTHR_EV_RD_Y)
#define evthr_got_wr(task) evthr_ev_occ((task), EVTHR_EV_WR_Y)
#define evthr_got_li(task) evthr_ev_occ((task), EVTHR_EV_LI_Y)
#define evthr_got_slp(task) evthr_ev_occ((task), EVTHR_EV_SL_Y)
#define evthr_got_sg(task) evthr_ev_occ((task), EVTHR_EV_SG_Y)
#define evthr_got_wk(task) evthr_ev_occ((task), EVTHR_EV_WK_Y)
#define evthr_is_evf(r, w) (((r) & (w)) != 0)
#define evthr_is_rdf(r) evthr_is_evf((r), EVTHR_EV_RD)
#define evthr_is_wrf(r) evthr_is_evf((r), EVTHR_EV_WR)
#define evthr_is_lif(r) evthr_is_evf((r), EVTHR_EV_LI)
#define evthr_is_iof(r) evthr_is_evf((r), EVTHR_EV_IOM)
#define evthr_is_slpf(r) evthr_is_evf((r), EVTHR_EV_SL)
#define evthr_is_sgf(r) evthr_is_evf((r), EVTHR_EV_SG)
#define evthr_is_wkf(r) evthr_is_evf((r), EVTHR_EV_WK)
#define evthr_got_rdf(r) evthr_is_evf((r), EVTHR_EV_RD_Y)
#define evthr_got_wrf(r) evthr_is_evf((r), EVTHR_EV_WR_Y)
#define evthr_got_lif(r) evthr_is_evf((r), EVTHR_EV_LI_Y)
#define evthr_got_slpf(r) evthr_is_evf((r), EVTHR_EV_SL_Y)
/* flags for task */
#define EVTHRT_FL_NONE 0x00000000
#define EVTHRT_FL_BLK_RD 0x00000001 /* task could block after read */
#define EVTHRT_FL_BLK_WR 0x00000002 /* task could block after write */
#define EVTHRT_FL_BLK_SL 0x00000004 /* task could block after sleep */
#define EVTHRT_FL_BLK_LI 0x00000008 /* task could block after listen */
#define EVTHRT_FL_BLOCK 0x0000000F /* task could block (| of previous values) */
#define EVTHRT_SET_FLAG(evthr_task, fl) (evthr_task)->evthr_t_flags |= (fl)
#define EVTHRT_CLR_FLAG(evthr_task, fl) (evthr_task)->evthr_t_flags &= ~(fl)
#define EVTHRT_IS_FLAG(evthr_task, fl) (((evthr_task)->evthr_t_flags & (fl)) != 0)
/* "why" parameter for evthr_term() and other read()/write() args */
#define EVTHR_STOP 'S'
#define EVTHR_ABRT 'A'
#define EVTHR_CONT 'C' /* just wake up scheduler (continue) */
#define EVTHR_USR1 '1'
#define EVTHR_USR2 '2'
#define EVTHR_ERROR 'E' /* check error variables */
#define EVTHR_SIG2IDX(sig) ((sig) == SIGUSR1 ? 0 : ((sig) == SIGUSR2 ? 1 : (-1)))
#define EVTHR_WHY2IDX(why) ((why) == EVTHR_USR1 ? 0 : ((why) == EVTHR_USR2 ? 1 : (-1)))
#define EVTHR_IDX2SIG(idx) ((idx) == 0 ? SIGUSR1 : ((idx) == 1 ? SIGUSR2 : (-1)))
/* result of evthr_task */
#define EVTHR_OK 0x0000 /* do nothing, task has been taken care of */
#define EVTHR_WAITQ 0x0001 /* put in waitq */
#define EVTHR_RUNQ 0x0002 /* put in runq */
#define EVTHR_SLPQ 0x0003 /* sleep for a while */
#define EVTHR_DEL 0x0004 /* delete task */
#define EVTHR_ACT_M 0x0007 /* action mask */
#define EVTHR_TERM 0x1000 /* terminate event thread loop */
#define EVTHR_S_M 0x00F0 /* mask for "wants X" */
#define EVTHR_C_M 0x0F00 /* mask for "clear X" */
#define EVTHR_S_OFF 4 /* shift offset for "wants X" */
#define EVTHR_C_OFF 8 /* shift offset for "clear X" */
#define EVTHR_FL_M (EVTHR_S_M|EVTHR_C_M) /* flag mask */
#define evthr_r_get_fl(r) ((r) & EVTHR_FL_M)
#define evthr_r_set_fl(r, fl) (r) |= (fl) & EVTHR_FL_M
#define evthr_act_waitq(r) (((r) & EVTHR_ACT_M) == EVTHR_WAITQ)
#define evthr_act_runq(r) (((r) & EVTHR_ACT_M) == EVTHR_RUNQ)
#define evthr_act_slpq(r) (((r) & EVTHR_ACT_M) == EVTHR_SLPQ)
#define evthr_act_del(r) (((r) & EVTHR_ACT_M) == EVTHR_DEL)
#define evthr_act_async(r) (((r) & EVTHR_ACT_M) == EVTHR_OK)
#define evthr_act_term(r) (((r) & EVTHR_TERM) != 0)
#define evthr_r_set(r) (((r) & EVTHR_S_M) != 0)
#define evthr_r_clr(r) (((r) & EVTHR_C_M) != 0)
#define evthr_r_set_ev(r) (((r) & EVTHR_S_M) >> EVTHR_S_OFF)
#define evthr_r_clr_ev(r) (((r) & EVTHR_C_M) >> EVTHR_C_OFF)
/* macros to construct return value */
#define evthr_r_yes(ev) (((ev) << EVTHR_S_OFF) & EVTHR_S_M)
#define evthr_r_no(ev) (((ev) << EVTHR_C_OFF) & EVTHR_C_M)
/* macros to manipulate the queues */
#if EVTHR_PRIOS <= 1
#define EVTHR_RUNQ_INIT(ctx) CIRCLEQ_INIT(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_FIRST(ctx, prio) CIRCLEQ_FIRST(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_LAST(ctx, prio) CIRCLEQ_LAST(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_END(ctx, prio) CIRCLEQ_END(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_EMPTY_ALL(ctx, prio) CIRCLEQ_EMPTY(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_EMPTY(ctx, prio) CIRCLEQ_EMPTY(&((ctx)->evthr_c_runq))
#define EVTHR_RUNQ_LOOP(ctx, prio, task) CIRCLEQ_FOREACH(task, &((ctx)->evthr_c_runq), evthr_t_next)
#define EVTHR_RUNQ_APP(ctx, prio, task) CIRCLEQ_INSERT_TAIL(&((ctx)->evthr_c_runq), task, evthr_t_next)
#define EVTHR_RUNQ_PRE(ctx, prio, task) CIRCLEQ_INSERT_HEAD(&((ctx)->evthr_c_runq), task, evthr_t_next)
#define EVTHR_RUNQ_INS(ctx, prio, task, new) CIRCLEQ_INSERT_BEFORE(&((ctx)->evthr_c_runq), (task), (new), evthr_t_next)
#define EVTHR_RUNQ_DEL(ctx, prio, task) CIRCLEQ_REMOVE(&((ctx)->evthr_c_runq), task, evthr_t_next)
#else /* EVTHR_PRIOS <= 1 */
#define EVTHR_RUNQ_INIT(ctx) do { \
int i; \
for (i = 0; i < EVTHR_PRIOS; i++) \
CIRCLEQ_INIT(&((ctx)->evthr_c_runq[i])); \
} while (0)
#define EVTHR_RUNQ_FIRST(ctx, prio) CIRCLEQ_FIRST(&((ctx)->evthr_c_runq[prio]))
#define EVTHR_RUNQ_LAST(ctx, prio) CIRCLEQ_LAST(&((ctx)->evthr_c_runq[prio]))
#define EVTHR_RUNQ_END(ctx, prio) CIRCLEQ_END(&((ctx)->evthr_c_runq[prio]))
#define EVTHR_RUNQ_EMPTY_ALL(ctx, prio) evthr_runq_empty_all((ctx), &(prio))
#define EVTHR_RUNQ_EMPTY(ctx, prio) CIRCLEQ_EMPTY(&((ctx)->evthr_c_runq[prio]))
#define EVTHR_RUNQ_LOOP(ctx, prio, task) CIRCLEQ_FOREACH(task, &((ctx)->evthr_c_runq[prio]), evthr_t_next)
#define EVTHR_RUNQ_APP(ctx, prio, task) CIRCLEQ_INSERT_TAIL(&((ctx)->evthr_c_runq[prio]), task, evthr_t_next)
#define EVTHR_RUNQ_PRE(ctx, prio, task) CIRCLEQ_INSERT_HEAD(&((ctx)->evthr_c_runq[prio]), task, evthr_t_next)
#define EVTHR_RUNQ_INS(ctx, prio, task, new) CIRCLEQ_INSERT_BEFORE(&((ctx)->evthr_c_runq[prio]), (task), (new), evthr_t_next)
#define EVTHR_RUNQ_DEL(ctx, prio, task) CIRCLEQ_REMOVE(&((ctx)->evthr_c_runq[prio]), task, evthr_t_next)
#endif /* EVTHR_PRIOS <= 1 */
#define EVTHR_WAITQ_INIT(ctx) CIRCLEQ_INIT(&((ctx)->evthr_c_waitq))
#define EVTHR_REQ_INIT(ctx) CIRCLEQ_INIT(&((ctx)->evthr_c_reqs))
#define EVTHR_WAITQ_FIRST(ctx) CIRCLEQ_FIRST(&((ctx)->evthr_c_waitq))
#define EVTHR_REQ_FIRST(ctx) CIRCLEQ_FIRST(&((ctx)->evthr_c_reqs))
#define EVTHR_WAITQ_LAST(ctx) CIRCLEQ_LAST(&((ctx)->evthr_c_waitq))
#define EVTHR_REQ_LAST(ctx) CIRCLEQ_LAST(&((ctx)->evthr_c_reqs))
#define EVTHR_WAITQ_END(ctx) CIRCLEQ_END(&((ctx)->evthr_c_waitq))
#define EVTHR_REQ_END(ctx) CIRCLEQ_END(&((ctx)->evthr_c_reqs))
#define EVTHR_WAITQ_EMPTY(ctx) CIRCLEQ_EMPTY(&((ctx)->evthr_c_waitq))
#define EVTHR_REQ_EMPTY(ctx) CIRCLEQ_EMPTY(&((ctx)->evthr_c_reqs))
#define EVTHR_RUNQ_NEXT(task) CIRCLEQ_NEXT(task, evthr_t_next)
#define EVTHR_WAITQ_NEXT(task) CIRCLEQ_NEXT(task, evthr_t_next)
#define EVTHR_REQ_NEXT(req) CIRCLEQ_NEXT(req, evthr_r_next)
#define EVTHR_WAITQ_PREV(task) CIRCLEQ_PREV(task, evthr_t_next)
#define EVTHR_WAITQ_LOOP(ctx, task) CIRCLEQ_FOREACH(task, &((ctx)->evthr_c_waitq), evthr_t_next)
#define EVTHR_WAITQ_APP(ctx, task) do { \
SM_ASSERT(!evthr_is_inwq(task)); \
CIRCLEQ_INSERT_TAIL(&((ctx)->evthr_c_waitq), task, evthr_t_next); \
} while (0)
#define EVTHR_REQ_APP(ctx, req) CIRCLEQ_INSERT_TAIL(&((ctx)->evthr_c_reqs), req, evthr_r_next)
#define EVTHR_WAITQ_PRE(ctx, task) do { \
SM_ASSERT(!evthr_is_inwq(task)); \
CIRCLEQ_INSERT_HEAD(&((ctx)->evthr_c_waitq), task, evthr_t_next); \
} while (0)
#define EVTHR_WAITQ_INS(ctx, task_old, task_new) do { \
SM_ASSERT(!evthr_is_inwq(task_new)); \
CIRCLEQ_INSERT_BEFORE(&((ctx)->evthr_c_waitq), (task_old), (task_new), evthr_t_next); \
} while (0)
#define EVTHR_WAITQ_DEL(ctx, task) CIRCLEQ_REMOVE(&((ctx)->evthr_c_waitq), task, evthr_t_next)
#define EVTHR_REQ_DEL(ctx, req) CIRCLEQ_REMOVE(&((ctx)->evthr_c_reqs), req, evthr_r_next)
#if 0
#define EVTHR_CTX_REMOVE_FREE(addr, tok) do { \
CIRCLEQ_REMOVE(&((addr)->sm_a2821_hd), tok, sm_t2821_l); \
t2821_free((addr)->sm_a2821_rpool, tok); \
} while (0)
#endif /* 0 */
/* queues */
/* function prototypes */
sm_ret_T evthr_init(sm_evthr_ctx_P *_pctx, uint _minthr, uint _maxthr, uint _maxfd);
sm_ret_T evthr_comptoptions(sm_evthr_ctx_P _evthr_ctx, sm_file_T *_fp);
sm_ret_T evthr_set_max_h(sm_evthr_ctx_P _ctx, uint _maxthr_h);
sm_ret_T evthr_set_max_s(sm_evthr_ctx_P _ctx, uint _maxthr_s);
sm_ret_T evthr_task_new(sm_evthr_ctx_P _evthr_ctx, sm_evthr_task_P *_ptask, int _ev, int _fd, timeval_T *_sleept, evthr_task_F *_fct, void *_taskctx);
sm_ret_T evthr_loop(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_timeval(sm_evthr_ctx_P _ctx, timeval_T *_ct);
time_T evthr_time(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_waitq_app(sm_evthr_task_P _task);
sm_ret_T evthr_en_wr(sm_evthr_task_P _task);
sm_ret_T evthr_new_sl(sm_evthr_task_P _task, timeval_T _slpt, bool _change);
sm_ret_T evthr_a2del(sm_evthr_task_P _task);
sm_ret_T evthr_wakeup_task(sm_evthr_task_P _task);
/* internal functions, but also used by libdns */
sm_ret_T evthr_slpq_ins(sm_evthr_ctx_P _ctx, sm_evthr_task_P _task);
sm_ret_T evthr_task_del(sm_evthr_ctx_P _ctx, sm_evthr_task_P _task, thr_lock_T _locktype);
#if 0
sm_ret_T evthr_gotsignal(sm_evthr_ctx_P _ctx, int _why);
#endif
sm_ret_T evthr_stop(sm_evthr_ctx_P _ctx);
void *evthr_worker(void *_ctx);
sm_evthr_req_P evthr_req_new(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_reqs_free(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_signal_init(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_set_dbglvl(sm_evthr_ctx_P _ctx, uint _dbglvl);
sm_ret_T evthr_before_block(sm_evthr_ctx_P _ctx);
sm_ret_T evthr_after_block(sm_evthr_ctx_P _ctx);
#endif /* SM_EVTHR_H */
syntax highlighted by Code2HTML, v. 0.9.1