blob: 8b6a7e2f59734af8902bef748fb9811130e83393 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* Thread management routine
2 * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22/* #define DEBUG */
23
24#include <zebra.h>
25
26#include "thread.h"
27#include "memory.h"
28#include "log.h"
paule04ab742003-01-17 23:47:00 +000029#include "hash.h"
30#include "command.h"
paul05c447d2004-07-22 19:14:27 +000031#include "sigevent.h"
paule04ab742003-01-17 23:47:00 +000032
ajs8b70d0b2005-04-28 01:31:13 +000033struct timeval recent_time;
paule04ab742003-01-17 23:47:00 +000034static struct hash *cpu_record = NULL;
paul718e3742002-12-13 20:15:29 +000035
36/* Struct timeval's tv_usec one second value. */
37#define TIMER_SECOND_MICRO 1000000L
38
ajs8b70d0b2005-04-28 01:31:13 +000039/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
40 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000041static struct timeval
paul718e3742002-12-13 20:15:29 +000042timeval_adjust (struct timeval a)
43{
44 while (a.tv_usec >= TIMER_SECOND_MICRO)
45 {
46 a.tv_usec -= TIMER_SECOND_MICRO;
47 a.tv_sec++;
48 }
49
50 while (a.tv_usec < 0)
51 {
52 a.tv_usec += TIMER_SECOND_MICRO;
53 a.tv_sec--;
54 }
55
56 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000057 /* Change negative timeouts to 0. */
58 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000059
60 return a;
61}
62
63static struct timeval
64timeval_subtract (struct timeval a, struct timeval b)
65{
66 struct timeval ret;
67
68 ret.tv_usec = a.tv_usec - b.tv_usec;
69 ret.tv_sec = a.tv_sec - b.tv_sec;
70
71 return timeval_adjust (ret);
72}
73
ajs8b70d0b2005-04-28 01:31:13 +000074static long
paul718e3742002-12-13 20:15:29 +000075timeval_cmp (struct timeval a, struct timeval b)
76{
77 return (a.tv_sec == b.tv_sec
78 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
79}
80
81static unsigned long
82timeval_elapsed (struct timeval a, struct timeval b)
83{
84 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
85 + (a.tv_usec - b.tv_usec));
86}
87
paula48b4e62005-04-22 00:43:47 +000088static unsigned int
paule04ab742003-01-17 23:47:00 +000089cpu_record_hash_key (struct cpu_thread_history *a)
90{
paul8cc41982005-05-06 21:25:49 +000091 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +000092}
93
94static int
95cpu_record_hash_cmp (struct cpu_thread_history *a,
96 struct cpu_thread_history *b)
97{
98 return a->func == b->func;
99}
100
paul8cc41982005-05-06 21:25:49 +0000101static void *
paule04ab742003-01-17 23:47:00 +0000102cpu_record_hash_alloc (struct cpu_thread_history *a)
103{
104 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000105 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000106 new->func = a->func;
paul9d11a192004-10-31 16:19:24 +0000107 new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname);
paule04ab742003-01-17 23:47:00 +0000108 return new;
109}
110
111static inline void
112vty_out_cpu_thread_history(struct vty* vty,
113 struct cpu_thread_history *a)
114{
ajs8b70d0b2005-04-28 01:31:13 +0000115#ifdef HAVE_RUSAGE
116 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
117 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
118 a->cpu.total/a->total_calls, a->cpu.max,
119 a->real.total/a->total_calls, a->real.max);
120#else
121 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
122 a->real.total/1000, a->real.total%1000, a->total_calls,
123 a->real.total/a->total_calls, a->real.max);
124#endif
125 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000126 a->types & (1 << THREAD_READ) ? 'R':' ',
127 a->types & (1 << THREAD_WRITE) ? 'W':' ',
128 a->types & (1 << THREAD_TIMER) ? 'T':' ',
129 a->types & (1 << THREAD_EVENT) ? 'E':' ',
130 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000131 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000132 a->funcname, VTY_NEWLINE);
133}
134
135static void
136cpu_record_hash_print(struct hash_backet *bucket,
137 void *args[])
138{
139 struct cpu_thread_history *totals = args[0];
140 struct vty *vty = args[1];
141 unsigned char *filter = args[2];
142 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000143
paule04ab742003-01-17 23:47:00 +0000144 a = bucket->data;
145 if ( !(a->types & *filter) )
146 return;
147 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000148 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000149 totals->real.total += a->real.total;
150 if (totals->real.max < a->real.max)
151 totals->real.max = a->real.max;
152#ifdef HAVE_RUSAGE
153 totals->cpu.total += a->cpu.total;
154 if (totals->cpu.max < a->cpu.max)
155 totals->cpu.max = a->cpu.max;
156#endif
paule04ab742003-01-17 23:47:00 +0000157}
158
159static void
160cpu_record_print(struct vty *vty, unsigned char filter)
161{
162 struct cpu_thread_history tmp;
163 void *args[3] = {&tmp, vty, &filter};
164
165 memset(&tmp, 0, sizeof tmp);
166 tmp.funcname = "TOTAL";
167 tmp.types = filter;
168
ajs8b70d0b2005-04-28 01:31:13 +0000169#ifdef HAVE_RUSAGE
170 vty_out(vty, "%21s %18s %18s%s",
171 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
172#endif
173 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
174#ifdef HAVE_RUSAGE
175 vty_out(vty, " Avg uSec Max uSecs");
176#endif
177 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000178 hash_iterate(cpu_record,
179 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
180 args);
181
182 if (tmp.total_calls > 0)
183 vty_out_cpu_thread_history(vty, &tmp);
184}
185
186DEFUN(show_thread_cpu,
187 show_thread_cpu_cmd,
188 "show thread cpu [FILTER]",
189 SHOW_STR
190 "Thread information\n"
191 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000192 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000193{
194 int i = 0;
195 unsigned char filter = 0xff;
196
197 if (argc > 0)
198 {
199 filter = 0;
200 while (argv[0][i] != '\0')
201 {
202 switch ( argv[0][i] )
203 {
204 case 'r':
205 case 'R':
206 filter |= (1 << THREAD_READ);
207 break;
208 case 'w':
209 case 'W':
210 filter |= (1 << THREAD_WRITE);
211 break;
212 case 't':
213 case 'T':
214 filter |= (1 << THREAD_TIMER);
215 break;
216 case 'e':
217 case 'E':
218 filter |= (1 << THREAD_EVENT);
219 break;
220 case 'x':
221 case 'X':
222 filter |= (1 << THREAD_EXECUTE);
223 break;
paula48b4e62005-04-22 00:43:47 +0000224 case 'b':
225 case 'B':
226 filter |= (1 << THREAD_BACKGROUND);
227 break;
paule04ab742003-01-17 23:47:00 +0000228 default:
229 break;
230 }
231 ++i;
232 }
233 if (filter == 0)
234 {
paula48b4e62005-04-22 00:43:47 +0000235 vty_out(vty, "Invalid filter \"%s\" specified,"
236 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000237 argv[0], VTY_NEWLINE);
238 return CMD_WARNING;
239 }
240 }
241
242 cpu_record_print(vty, filter);
243 return CMD_SUCCESS;
244}
245
paul718e3742002-12-13 20:15:29 +0000246/* List allocation and head/tail print out. */
247static void
248thread_list_debug (struct thread_list *list)
249{
250 printf ("count [%d] head [%p] tail [%p]\n",
251 list->count, list->head, list->tail);
252}
253
254/* Debug print for thread_master. */
paul8cc41982005-05-06 21:25:49 +0000255static void __attribute__ ((unused))
paul718e3742002-12-13 20:15:29 +0000256thread_master_debug (struct thread_master *m)
257{
258 printf ("-----------\n");
259 printf ("readlist : ");
260 thread_list_debug (&m->read);
261 printf ("writelist : ");
262 thread_list_debug (&m->write);
263 printf ("timerlist : ");
264 thread_list_debug (&m->timer);
265 printf ("eventlist : ");
266 thread_list_debug (&m->event);
267 printf ("unuselist : ");
268 thread_list_debug (&m->unuse);
paula48b4e62005-04-22 00:43:47 +0000269 printf ("bgndlist : ");
270 thread_list_debug (&m->background);
paul718e3742002-12-13 20:15:29 +0000271 printf ("total alloc: [%ld]\n", m->alloc);
272 printf ("-----------\n");
273}
274
275/* Allocate new thread master. */
276struct thread_master *
277thread_master_create ()
278{
paule04ab742003-01-17 23:47:00 +0000279 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000280 cpu_record
281 = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
282 (int (*) (void *, void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000283
paul718e3742002-12-13 20:15:29 +0000284 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
285 sizeof (struct thread_master));
286}
287
288/* Add a new thread to the list. */
289static void
290thread_list_add (struct thread_list *list, struct thread *thread)
291{
292 thread->next = NULL;
293 thread->prev = list->tail;
294 if (list->tail)
295 list->tail->next = thread;
296 else
297 list->head = thread;
298 list->tail = thread;
299 list->count++;
300}
301
302/* Add a new thread just before the point. */
303static void
304thread_list_add_before (struct thread_list *list,
305 struct thread *point,
306 struct thread *thread)
307{
308 thread->next = point;
309 thread->prev = point->prev;
310 if (point->prev)
311 point->prev->next = thread;
312 else
313 list->head = thread;
314 point->prev = thread;
315 list->count++;
316}
317
318/* Delete a thread from the list. */
319static struct thread *
320thread_list_delete (struct thread_list *list, struct thread *thread)
321{
322 if (thread->next)
323 thread->next->prev = thread->prev;
324 else
325 list->tail = thread->prev;
326 if (thread->prev)
327 thread->prev->next = thread->next;
328 else
329 list->head = thread->next;
330 thread->next = thread->prev = NULL;
331 list->count--;
332 return thread;
333}
334
335/* Move thread to unuse list. */
336static void
337thread_add_unuse (struct thread_master *m, struct thread *thread)
338{
paula48b4e62005-04-22 00:43:47 +0000339 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000340 assert (thread->next == NULL);
341 assert (thread->prev == NULL);
342 assert (thread->type == THREAD_UNUSED);
343 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000344 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000345}
346
347/* Free all unused thread. */
348static void
349thread_list_free (struct thread_master *m, struct thread_list *list)
350{
351 struct thread *t;
352 struct thread *next;
353
354 for (t = list->head; t; t = next)
355 {
356 next = t->next;
paul9d11a192004-10-31 16:19:24 +0000357 XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
paul718e3742002-12-13 20:15:29 +0000358 XFREE (MTYPE_THREAD, t);
359 list->count--;
360 m->alloc--;
361 }
362}
363
364/* Stop thread scheduler. */
365void
366thread_master_free (struct thread_master *m)
367{
368 thread_list_free (m, &m->read);
369 thread_list_free (m, &m->write);
370 thread_list_free (m, &m->timer);
371 thread_list_free (m, &m->event);
372 thread_list_free (m, &m->ready);
373 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000374 thread_list_free (m, &m->background);
375
paul718e3742002-12-13 20:15:29 +0000376 XFREE (MTYPE_THREAD_MASTER, m);
377}
378
paul8cc41982005-05-06 21:25:49 +0000379/* Thread list is empty or not. */
380static inline int
381thread_empty (struct thread_list *list)
382{
383 return list->head ? 0 : 1;
384}
385
paul718e3742002-12-13 20:15:29 +0000386/* Delete top of the list and return it. */
387static struct thread *
388thread_trim_head (struct thread_list *list)
389{
paul8cc41982005-05-06 21:25:49 +0000390 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000391 return thread_list_delete (list, list->head);
392 return NULL;
393}
394
paul718e3742002-12-13 20:15:29 +0000395/* Return remain time in second. */
396unsigned long
397thread_timer_remain_second (struct thread *thread)
398{
ajs8b70d0b2005-04-28 01:31:13 +0000399 gettimeofday (&recent_time, NULL);
paul718e3742002-12-13 20:15:29 +0000400
ajs8b70d0b2005-04-28 01:31:13 +0000401 if (thread->u.sands.tv_sec - recent_time.tv_sec > 0)
402 return thread->u.sands.tv_sec - recent_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000403 else
404 return 0;
405}
406
paule04ab742003-01-17 23:47:00 +0000407/* Trim blankspace and "()"s */
408static char *
hasso8c328f12004-10-05 21:01:23 +0000409strip_funcname (const char *funcname)
paule04ab742003-01-17 23:47:00 +0000410{
411 char buff[100];
412 char tmp, *ret, *e, *b = buff;
413
414 strncpy(buff, funcname, sizeof(buff));
415 buff[ sizeof(buff) -1] = '\0';
416 e = buff +strlen(buff) -1;
417
418 /* Wont work for funcname == "Word (explanation)" */
419
420 while (*b == ' ' || *b == '(')
421 ++b;
422 while (*e == ' ' || *e == ')')
423 --e;
424 e++;
425
426 tmp = *e;
427 *e = '\0';
paul9d11a192004-10-31 16:19:24 +0000428 ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
paule04ab742003-01-17 23:47:00 +0000429 *e = tmp;
430
431 return ret;
432}
433
paul718e3742002-12-13 20:15:29 +0000434/* Get new thread. */
435static struct thread *
436thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000437 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000438{
439 struct thread *thread;
440
paul8cc41982005-05-06 21:25:49 +0000441 if (!thread_empty (&m->unuse))
paul2946f652003-03-27 23:48:24 +0000442 {
443 thread = thread_trim_head (&m->unuse);
444 if (thread->funcname)
paul9d11a192004-10-31 16:19:24 +0000445 XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
paul2946f652003-03-27 23:48:24 +0000446 }
paul718e3742002-12-13 20:15:29 +0000447 else
448 {
449 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
450 m->alloc++;
451 }
452 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000453 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000454 thread->master = m;
455 thread->func = func;
456 thread->arg = arg;
457
paule04ab742003-01-17 23:47:00 +0000458 thread->funcname = strip_funcname(funcname);
459
paul718e3742002-12-13 20:15:29 +0000460 return thread;
461}
462
463/* Add new read thread. */
464struct thread *
paule04ab742003-01-17 23:47:00 +0000465funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000466 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000467{
468 struct thread *thread;
469
470 assert (m != NULL);
471
472 if (FD_ISSET (fd, &m->readfd))
473 {
474 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
475 return NULL;
476 }
477
paule04ab742003-01-17 23:47:00 +0000478 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000479 FD_SET (fd, &m->readfd);
480 thread->u.fd = fd;
481 thread_list_add (&m->read, thread);
482
483 return thread;
484}
485
486/* Add new write thread. */
487struct thread *
paule04ab742003-01-17 23:47:00 +0000488funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000489 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000490{
491 struct thread *thread;
492
493 assert (m != NULL);
494
495 if (FD_ISSET (fd, &m->writefd))
496 {
497 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
498 return NULL;
499 }
500
paule04ab742003-01-17 23:47:00 +0000501 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000502 FD_SET (fd, &m->writefd);
503 thread->u.fd = fd;
504 thread_list_add (&m->write, thread);
505
506 return thread;
507}
508
paul98c91ac2004-10-05 14:57:50 +0000509static struct thread *
510funcname_thread_add_timer_timeval (struct thread_master *m,
511 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000512 int type,
paul98c91ac2004-10-05 14:57:50 +0000513 void *arg,
514 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000515 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000516{
paul718e3742002-12-13 20:15:29 +0000517 struct thread *thread;
ajs8b70d0b2005-04-28 01:31:13 +0000518 struct timeval alarm_time;
paula48b4e62005-04-22 00:43:47 +0000519 struct thread_list *list;
paul718e3742002-12-13 20:15:29 +0000520 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000521
522 assert (m != NULL);
523
ajs8b70d0b2005-04-28 01:31:13 +0000524 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000525 assert (time_relative);
526
ajs8b70d0b2005-04-28 01:31:13 +0000527 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000528 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000529
530 /* Do we need jitter here? */
ajs8b70d0b2005-04-28 01:31:13 +0000531 gettimeofday (&recent_time, NULL);
532 alarm_time.tv_sec = recent_time.tv_sec + time_relative->tv_sec;
533 alarm_time.tv_usec = recent_time.tv_usec + time_relative->tv_usec;
534 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000535
536 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000537 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000538 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
539 break;
540
541 if (tt)
paula48b4e62005-04-22 00:43:47 +0000542 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000543 else
paula48b4e62005-04-22 00:43:47 +0000544 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000545
546 return thread;
547}
548
paul98c91ac2004-10-05 14:57:50 +0000549
550/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000551struct thread *
paul98c91ac2004-10-05 14:57:50 +0000552funcname_thread_add_timer (struct thread_master *m,
553 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000554 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000555{
paul98c91ac2004-10-05 14:57:50 +0000556 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000557
558 assert (m != NULL);
559
paul9076fbd2004-10-11 09:40:58 +0000560 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000561 trel.tv_usec = 0;
562
paula48b4e62005-04-22 00:43:47 +0000563 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
564 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000565}
566
567/* Add timer event thread with "millisecond" resolution */
568struct thread *
569funcname_thread_add_timer_msec (struct thread_master *m,
570 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000571 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000572{
573 struct timeval trel;
574
575 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000576
ajsaf04bd72004-12-28 17:00:12 +0000577 trel.tv_sec = timer / 1000;
578 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000579
paula48b4e62005-04-22 00:43:47 +0000580 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
581 arg, &trel, funcname);
582}
583
584/* Add a background thread, with an optional millisec delay */
585struct thread *
586funcname_thread_add_background (struct thread_master *m,
587 int (*func) (struct thread *),
588 void *arg, long delay,
589 const char *funcname)
590{
591 struct timeval trel;
592
593 assert (m != NULL);
594
595 if (delay)
596 {
597 trel.tv_sec = delay / 1000;
598 trel.tv_usec = 1000*(delay % 1000);
599 }
600 else
601 {
602 trel.tv_sec = 0;
603 trel.tv_usec = 0;
604 }
605
606 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
607 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000608}
609
paul718e3742002-12-13 20:15:29 +0000610/* Add simple event thread. */
611struct thread *
paule04ab742003-01-17 23:47:00 +0000612funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000613 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000614{
615 struct thread *thread;
616
617 assert (m != NULL);
618
paule04ab742003-01-17 23:47:00 +0000619 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000620 thread->u.val = val;
621 thread_list_add (&m->event, thread);
622
623 return thread;
624}
625
626/* Cancel thread from scheduler. */
627void
628thread_cancel (struct thread *thread)
629{
paula48b4e62005-04-22 00:43:47 +0000630 struct thread_list *list;
631
paul718e3742002-12-13 20:15:29 +0000632 switch (thread->type)
633 {
634 case THREAD_READ:
635 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
636 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000637 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000638 break;
639 case THREAD_WRITE:
640 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
641 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000642 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000643 break;
644 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000645 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000646 break;
647 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000648 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000649 break;
650 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000651 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000652 break;
paula48b4e62005-04-22 00:43:47 +0000653 case THREAD_BACKGROUND:
654 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000655 break;
paul718e3742002-12-13 20:15:29 +0000656 default:
paula48b4e62005-04-22 00:43:47 +0000657 return;
paul718e3742002-12-13 20:15:29 +0000658 break;
659 }
paula48b4e62005-04-22 00:43:47 +0000660 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000661 thread->type = THREAD_UNUSED;
662 thread_add_unuse (thread->master, thread);
663}
664
665/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000666unsigned int
paul718e3742002-12-13 20:15:29 +0000667thread_cancel_event (struct thread_master *m, void *arg)
668{
pauldc818072005-05-19 01:30:53 +0000669 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000670 struct thread *thread;
671
672 thread = m->event.head;
673 while (thread)
674 {
675 struct thread *t;
676
677 t = thread;
678 thread = t->next;
679
680 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000681 {
pauldc818072005-05-19 01:30:53 +0000682 ret++;
paula48b4e62005-04-22 00:43:47 +0000683 thread_list_delete (&m->event, t);
684 t->type = THREAD_UNUSED;
685 thread_add_unuse (m, t);
686 }
paul718e3742002-12-13 20:15:29 +0000687 }
pauldc818072005-05-19 01:30:53 +0000688 return ret;
paul718e3742002-12-13 20:15:29 +0000689}
690
paula48b4e62005-04-22 00:43:47 +0000691static struct timeval *
692thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000693{
paul8cc41982005-05-06 21:25:49 +0000694 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000695 {
ajs8b70d0b2005-04-28 01:31:13 +0000696 *timer_val = timeval_subtract (tlist->head->u.sands, recent_time);
paul718e3742002-12-13 20:15:29 +0000697 return timer_val;
698 }
699 return NULL;
700}
paul718e3742002-12-13 20:15:29 +0000701
paul8cc41982005-05-06 21:25:49 +0000702static struct thread *
paul718e3742002-12-13 20:15:29 +0000703thread_run (struct thread_master *m, struct thread *thread,
704 struct thread *fetch)
705{
706 *fetch = *thread;
707 thread->type = THREAD_UNUSED;
708 thread_add_unuse (m, thread);
709 return fetch;
710}
711
paula48b4e62005-04-22 00:43:47 +0000712static int
713thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000714{
715 struct thread *thread;
716 struct thread *next;
717 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000718
719 assert (list);
720
paul718e3742002-12-13 20:15:29 +0000721 for (thread = list->head; thread; thread = next)
722 {
723 next = thread->next;
724
725 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000726 {
727 assert (FD_ISSET (THREAD_FD (thread), mfdset));
728 FD_CLR(THREAD_FD (thread), mfdset);
729 thread_list_delete (list, thread);
730 thread_list_add (&thread->master->ready, thread);
731 thread->type = THREAD_READY;
732 ready++;
733 }
paul718e3742002-12-13 20:15:29 +0000734 }
735 return ready;
736}
737
ajs8b70d0b2005-04-28 01:31:13 +0000738/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000739static unsigned int
740thread_timer_process (struct thread_list *list, struct timeval *timenow)
741{
742 struct thread *thread;
743 unsigned int ready = 0;
744
paula48b4e62005-04-22 00:43:47 +0000745 for (thread = list->head; thread; thread = thread->next)
ajs8b70d0b2005-04-28 01:31:13 +0000746 {
747 if (timeval_cmp (*timenow, thread->u.sands) < 0)
748 return ready;
749 thread_list_delete (list, thread);
750 thread->type = THREAD_READY;
751 thread_list_add (&thread->master->ready, thread);
752 ready++;
753 }
paula48b4e62005-04-22 00:43:47 +0000754 return ready;
755}
756
paul718e3742002-12-13 20:15:29 +0000757/* Fetch next ready thread. */
758struct thread *
759thread_fetch (struct thread_master *m, struct thread *fetch)
760{
paul718e3742002-12-13 20:15:29 +0000761 struct thread *thread;
762 fd_set readfd;
763 fd_set writefd;
764 fd_set exceptfd;
paul718e3742002-12-13 20:15:29 +0000765 struct timeval timer_val;
paula48b4e62005-04-22 00:43:47 +0000766 struct timeval timer_val_bg;
paul718e3742002-12-13 20:15:29 +0000767 struct timeval *timer_wait;
paula48b4e62005-04-22 00:43:47 +0000768 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +0000769
770 while (1)
771 {
paula48b4e62005-04-22 00:43:47 +0000772 int num = 0;
paula48b4e62005-04-22 00:43:47 +0000773
paul05c447d2004-07-22 19:14:27 +0000774 /* Signals are highest priority */
775 quagga_sigevent_process ();
776
777 /* Normal event are the next highest priority. */
paul718e3742002-12-13 20:15:29 +0000778 if ((thread = thread_trim_head (&m->event)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000779 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000780
paula48b4e62005-04-22 00:43:47 +0000781 /* If there are any ready threads from previous scheduler runs,
782 * process top of them.
783 */
paul718e3742002-12-13 20:15:29 +0000784 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000785 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000786
paul718e3742002-12-13 20:15:29 +0000787 /* Structure copy. */
788 readfd = m->readfd;
789 writefd = m->writefd;
790 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +0000791
792 /* Calculate select wait timer if nothing else to do */
ajs8b70d0b2005-04-28 01:31:13 +0000793 gettimeofday (&recent_time, NULL);
794 timer_wait = thread_timer_wait (&m->timer, &timer_val);
795 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
796
797 if (timer_wait_bg &&
798 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
799 timer_wait = timer_wait_bg;
paula48b4e62005-04-22 00:43:47 +0000800
paul718e3742002-12-13 20:15:29 +0000801 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +0000802
803 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +0000804 if (num < 0)
paul05c447d2004-07-22 19:14:27 +0000805 {
806 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +0000807 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +0000808 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +0000809 return NULL;
810 }
ajs8b70d0b2005-04-28 01:31:13 +0000811
812 /* Check foreground timers. Historically, they have had higher
813 priority than I/O threads, so let's push them onto the ready
814 list in front of the I/O threads. */
815 gettimeofday (&recent_time, NULL);
816 thread_timer_process (&m->timer, &recent_time);
paula48b4e62005-04-22 00:43:47 +0000817
818 /* Got IO, process it */
819 if (num > 0)
820 {
821 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000822 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +0000823 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000824 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +0000825 }
ajs8b70d0b2005-04-28 01:31:13 +0000826
827#if 0
828 /* If any threads were made ready above (I/O or foreground timer),
829 perhaps we should avoid adding background timers to the ready
830 list at this time. If this is code is uncommented, then background
831 timer threads will not run unless there is nothing else to do. */
832 if ((thread = thread_trim_head (&m->ready)) != NULL)
833 return thread_run (m, thread, fetch);
834#endif
835
paula48b4e62005-04-22 00:43:47 +0000836 /* Background timer/events, lowest priority */
ajs8b70d0b2005-04-28 01:31:13 +0000837 thread_timer_process (&m->background, &recent_time);
paula48b4e62005-04-22 00:43:47 +0000838
ajs8b70d0b2005-04-28 01:31:13 +0000839 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000840 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +0000841 }
842}
843
ajs924b9222005-04-16 17:11:24 +0000844unsigned long
ajs8b70d0b2005-04-28 01:31:13 +0000845thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +0000846{
paul718e3742002-12-13 20:15:29 +0000847#ifdef HAVE_RUSAGE
848 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +0000849 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
850 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +0000851#else
ajs8b70d0b2005-04-28 01:31:13 +0000852 *cputime = 0;
paul718e3742002-12-13 20:15:29 +0000853#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +0000854 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +0000855}
856
ajs8b70d0b2005-04-28 01:31:13 +0000857/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
858 Note: we are using real (wall clock) time for this calculation.
859 It could be argued that CPU time may make more sense in certain
860 contexts. The things to consider are whether the thread may have
861 blocked (in which case wall time increases, but CPU time does not),
862 or whether the system is heavily loaded with other processes competing
863 for CPU time. On balance, wall clock time seems to make sense.
864 Plus it has the added benefit that gettimeofday should be faster
865 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +0000866int
867thread_should_yield (struct thread *thread)
868{
ajs8b70d0b2005-04-28 01:31:13 +0000869 gettimeofday(&recent_time, NULL);
870 return (timeval_elapsed(recent_time, thread->ru.real) >
871 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +0000872}
873
874/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +0000875 use that to get in-depth stats on the performance of the thread in addition
876 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +0000877void
878thread_call (struct thread *thread)
879{
ajs8b70d0b2005-04-28 01:31:13 +0000880 unsigned long realtime, cputime;
paul718e3742002-12-13 20:15:29 +0000881 RUSAGE_T ru;
Paul Jakmacc8b13a2006-07-25 20:40:40 +0000882
883 /* Cache a pointer to the relevant cpu history thread, if the thread
884 * does not have it yet.
885 *
886 * Callers submitting 'dummy threads' hence must take care that
887 * thread->cpu is NULL
888 */
889 if (!thread->hist)
890 {
891 struct cpu_thread_history tmp;
892
893 tmp.func = thread->func;
894 tmp.funcname = thread->funcname;
895
896 thread->hist = hash_get (cpu_record, &tmp,
897 (void * (*) (void *))cpu_record_hash_alloc);
898 }
paul718e3742002-12-13 20:15:29 +0000899
900 GETRUSAGE (&thread->ru);
901
902 (*thread->func) (thread);
903
904 GETRUSAGE (&ru);
905
ajs8b70d0b2005-04-28 01:31:13 +0000906 realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +0000907 thread->hist->real.total += realtime;
908 if (thread->hist->real.max < realtime)
909 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +0000910#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +0000911 thread->hist->cpu.total += cputime;
912 if (thread->hist->cpu.max < cputime)
913 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +0000914#endif
paule04ab742003-01-17 23:47:00 +0000915
Paul Jakmacc8b13a2006-07-25 20:40:40 +0000916 ++(thread->hist->total_calls);
917 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +0000918
ajs924b9222005-04-16 17:11:24 +0000919#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +0000920 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +0000921 {
922 /*
923 * We have a CPU Hog on our hands.
924 * Whinge about it now, so we're aware this is yet another task
925 * to fix.
926 */
ajs8b70d0b2005-04-28 01:31:13 +0000927 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +0000928 thread->funcname,
929 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +0000930 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +0000931 }
ajs924b9222005-04-16 17:11:24 +0000932#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +0000933}
934
935/* Execute thread */
936struct thread *
paule04ab742003-01-17 23:47:00 +0000937funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +0000938 int (*func)(struct thread *),
939 void *arg,
paule04ab742003-01-17 23:47:00 +0000940 int val,
hasso8c328f12004-10-05 21:01:23 +0000941 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000942{
943 struct thread dummy;
944
945 memset (&dummy, 0, sizeof (struct thread));
946
947 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +0000948 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +0000949 dummy.master = NULL;
950 dummy.func = func;
951 dummy.arg = arg;
952 dummy.u.val = val;
paule04ab742003-01-17 23:47:00 +0000953 dummy.funcname = strip_funcname (funcname);
paul718e3742002-12-13 20:15:29 +0000954 thread_call (&dummy);
955
paul9d11a192004-10-31 16:19:24 +0000956 XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
paul2946f652003-03-27 23:48:24 +0000957
paul718e3742002-12-13 20:15:29 +0000958 return NULL;
959}