Resync patch with contrib.
[dragonfly.git] / contrib / sendmail-8.14 / libmilter / monitor.c
blob3d23f0c27e4a4d8ca2b99ba6ac385b544f1f3033
1 /*
2 * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
9 */
11 #include "libmilter.h"
13 #if _FFR_THREAD_MONITOR
16 ** Thread Monitoring
17 ** Todo: more error checking (return code from function calls)
18 ** add comments.
21 bool Monitor = false; /* use monitoring? */
22 static unsigned int Mon_exec_time = 0;
24 /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
25 static smutex_t Mon_mutex;
26 static scond_t Mon_cv;
29 ** Current ctx to monitor.
30 ** Invariant:
31 ** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
32 ** time ago.
34 ** Basically the entries in the list are ordered by time because new
35 ** entries are appended at the end. However, due to the concurrent
36 ** execution (multi-threaded) and no guaranteed order of wakeups
37 ** after a mutex_lock() attempt, the order might not be strict,
38 ** i.e., if the list contains e1 and e2 (in that order) then
39 ** the the start time of e2 can be (slightly) smaller than that of e1.
40 ** However, this slight inaccurracy should not matter for the proper
41 ** working of this algorithm.
44 static SMFICTX_PTR Mon_cur_ctx = NULL;
45 static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
48 ** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
50 ** Parameters:
51 ** tm -- maximum execution time for a thread
53 ** Returns:
54 ** MI_SUCCESS
57 int
58 smfi_set_max_exec_time(tm)
59 unsigned int tm;
61 Mon_exec_time = tm;
62 return MI_SUCCESS;
66 ** MI_MONITOR_THREAD -- monitoring thread
68 ** Parameters:
69 ** arg -- ignored (required by pthread_create())
71 ** Returns:
72 ** NULL on termination.
75 static void *
76 mi_monitor_thread(arg)
77 void *arg;
79 sthread_t tid;
80 int r;
81 time_t now, end;
83 SM_ASSERT(Monitor);
84 SM_ASSERT(Mon_exec_time > 0);
85 tid = (sthread_t) sthread_get_id();
86 if (pthread_detach(tid) != 0)
88 /* log an error */
89 return (void *)1;
93 ** NOTE: this is "flow through" code,
94 ** do NOT use do { } while ("break" is used here!)
97 #define MON_CHK_STOP \
98 now = time(NULL); \
99 end = Mon_cur_ctx->ctx_start + Mon_exec_time; \
100 if (now > end) \
102 smi_log(SMI_LOG_ERR, \
103 "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
104 (long) now, (long) end, \
105 (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
106 mi_stop_milters(MILTER_STOP); \
107 break; \
110 (void) smutex_lock(&Mon_mutex);
111 while (mi_stop() == MILTER_CONT)
113 if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
115 struct timespec abstime;
117 MON_CHK_STOP;
118 abstime.tv_sec = end;
119 abstime.tv_nsec = 0;
120 r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
121 &abstime);
123 else
124 r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
125 if (mi_stop() != MILTER_CONT)
126 break;
127 if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
129 MON_CHK_STOP;
132 (void) smutex_unlock(&Mon_mutex);
134 return NULL;
138 ** MI_MONITOR_INIT -- initialize monitoring thread
140 ** Parameters: none
142 ** Returns:
143 ** MI_SUCCESS/MI_FAILURE
147 mi_monitor_init()
149 int r;
150 sthread_t tid;
152 SM_ASSERT(!Monitor);
153 if (Mon_exec_time <= 0)
154 return MI_SUCCESS;
155 Monitor = true;
156 if (!smutex_init(&Mon_mutex))
157 return MI_FAILURE;
158 if (scond_init(&Mon_cv) != 0)
159 return MI_FAILURE;
160 SM_TAILQ_INIT(&Mon_ctx_head);
162 r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
163 if (r != 0)
164 return r;
165 return MI_SUCCESS;
169 ** MI_MONITOR_WORK_BEGIN -- record start of thread execution
171 ** Parameters:
172 ** ctx -- session context
173 ** cmd -- milter command char
175 ** Returns:
176 ** 0
180 mi_monitor_work_begin(ctx, cmd)
181 SMFICTX_PTR ctx;
182 int cmd;
184 (void) smutex_lock(&Mon_mutex);
185 if (NULL == Mon_cur_ctx)
187 Mon_cur_ctx = ctx;
188 (void) scond_signal(&Mon_cv);
190 ctx->ctx_start = time(NULL);
191 SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
192 (void) smutex_unlock(&Mon_mutex);
193 return 0;
197 ** MI_MONITOR_WORK_END -- record end of thread execution
199 ** Parameters:
200 ** ctx -- session context
201 ** cmd -- milter command char
203 ** Returns:
204 ** 0
208 mi_monitor_work_end(ctx, cmd)
209 SMFICTX_PTR ctx;
210 int cmd;
212 (void) smutex_lock(&Mon_mutex);
213 ctx->ctx_start = 0;
214 SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
215 if (Mon_cur_ctx == ctx)
217 if (SM_TAILQ_EMPTY(&Mon_ctx_head))
218 Mon_cur_ctx = NULL;
219 else
220 Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
222 (void) smutex_unlock(&Mon_mutex);
223 return 0;
225 #endif /* _FFR_THREAD_MONITOR */