monitor.c revision 168515
1/*
2 *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
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.
8 *
9 */
10
11#include "libmilter.h"
12
13#if _FFR_THREAD_MONITOR
14
15/*
16**  Thread Monitoring
17**  Todo: more error checking (return code from function calls)
18**  add comments.
19*/
20
21bool Monitor = false; /* use monitoring? */
22static unsigned int Mon_exec_time = 0;
23
24/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
25static smutex_t Mon_mutex;
26static scond_t Mon_cv;
27
28/*
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.
33**
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.
42*/
43
44static SMFICTX_PTR Mon_cur_ctx = NULL;
45static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
46
47/*
48**  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
49**
50**	Parameters:
51**		tm -- maximum execution time for a thread
52**
53**	Returns:
54**		MI_SUCCESS
55*/
56
57int
58smfi_set_max_exec_time(tm)
59	unsigned int tm;
60{
61	Mon_exec_time = tm;
62	return MI_SUCCESS;
63}
64
65/*
66**  MI_MONITOR_THREAD -- monitoring thread
67**
68**	Parameters:
69**		arg -- ignored (required by pthread_create())
70**
71**	Returns:
72**		NULL on termination.
73*/
74
75static void *
76mi_monitor_thread(arg)
77	void *arg;
78{
79	sthread_t tid;
80	int r;
81	time_t now, end;
82
83	SM_ASSERT(Monitor);
84	SM_ASSERT(Mon_exec_time > 0);
85	tid = (sthread_t) sthread_get_id();
86	if (pthread_detach(tid) != 0)
87	{
88		/* log an error */
89		return (void *)1;
90	}
91
92/*
93**  NOTE: this is "flow through" code,
94**  do NOT use do { } while ("break" is used here!)
95*/
96
97#define MON_CHK_STOP							\
98	now = time(NULL);						\
99	end = Mon_cur_ctx->ctx_start + Mon_exec_time;			\
100	if (now > end)							\
101	{								\
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;							\
108	}
109
110	(void) smutex_lock(&Mon_mutex);
111	while (mi_stop() == MILTER_CONT)
112	{
113		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
114		{
115			struct timespec abstime;
116
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);
122		}
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)
128		{
129			MON_CHK_STOP;
130		}
131	}
132	(void) smutex_unlock(&Mon_mutex);
133
134	return NULL;
135}
136
137/*
138**  MI_MONITOR_INIT -- initialize monitoring thread
139**
140**	Parameters: none
141**
142**	Returns:
143**		MI_SUCCESS/MI_FAILURE
144*/
145
146int
147mi_monitor_init()
148{
149	int r;
150	sthread_t tid;
151
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);
161
162	r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
163	if (r != 0)
164		return r;
165	return MI_SUCCESS;
166}
167
168/*
169**  MI_MONITOR_WORK_BEGIN -- record start of thread execution
170**
171**	Parameters:
172**		ctx -- session context
173**		cmd -- milter command char
174**
175**	Returns:
176**		0
177*/
178
179int
180mi_monitor_work_begin(ctx, cmd)
181	SMFICTX_PTR ctx;
182	int cmd;
183{
184	(void) smutex_lock(&Mon_mutex);
185	if (NULL == Mon_cur_ctx)
186	{
187		Mon_cur_ctx = ctx;
188		(void) scond_signal(&Mon_cv);
189	}
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;
194}
195
196/*
197**  MI_MONITOR_WORK_END -- record end of thread execution
198**
199**	Parameters:
200**		ctx -- session context
201**		cmd -- milter command char
202**
203**	Returns:
204**		0
205*/
206
207int
208mi_monitor_work_end(ctx, cmd)
209	SMFICTX_PTR ctx;
210	int cmd;
211{
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)
216	{
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);
221	}
222	(void) smutex_unlock(&Mon_mutex);
223	return 0;
224}
225#endif /* _FFR_THREAD_MONITOR */
226