/* * 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 */