blob: 2e1dd2452e1e20b1266bc374e75f991887991385 [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{
91 return (unsigned int) a->func;
92}
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
101static void*
102cpu_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. */
255void
256thread_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)
paula48b4e62005-04-22 00:43:47 +0000280 cpu_record = hash_create_size (1011, cpu_record_hash_key,
281 cpu_record_hash_cmp);
282
paul718e3742002-12-13 20:15:29 +0000283 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
284 sizeof (struct thread_master));
285}
286
287/* Add a new thread to the list. */
288static void
289thread_list_add (struct thread_list *list, struct thread *thread)
290{
291 thread->next = NULL;
292 thread->prev = list->tail;
293 if (list->tail)
294 list->tail->next = thread;
295 else
296 list->head = thread;
297 list->tail = thread;
298 list->count++;
299}
300
301/* Add a new thread just before the point. */
302static void
303thread_list_add_before (struct thread_list *list,
304 struct thread *point,
305 struct thread *thread)
306{
307 thread->next = point;
308 thread->prev = point->prev;
309 if (point->prev)
310 point->prev->next = thread;
311 else
312 list->head = thread;
313 point->prev = thread;
314 list->count++;
315}
316
317/* Delete a thread from the list. */
318static struct thread *
319thread_list_delete (struct thread_list *list, struct thread *thread)
320{
321 if (thread->next)
322 thread->next->prev = thread->prev;
323 else
324 list->tail = thread->prev;
325 if (thread->prev)
326 thread->prev->next = thread->next;
327 else
328 list->head = thread->next;
329 thread->next = thread->prev = NULL;
330 list->count--;
331 return thread;
332}
333
334/* Move thread to unuse list. */
335static void
336thread_add_unuse (struct thread_master *m, struct thread *thread)
337{
paula48b4e62005-04-22 00:43:47 +0000338 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000339 assert (thread->next == NULL);
340 assert (thread->prev == NULL);
341 assert (thread->type == THREAD_UNUSED);
342 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000343 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000344}
345
346/* Free all unused thread. */
347static void
348thread_list_free (struct thread_master *m, struct thread_list *list)
349{
350 struct thread *t;
351 struct thread *next;
352
353 for (t = list->head; t; t = next)
354 {
355 next = t->next;
paul9d11a192004-10-31 16:19:24 +0000356 XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
paul718e3742002-12-13 20:15:29 +0000357 XFREE (MTYPE_THREAD, t);
358 list->count--;
359 m->alloc--;
360 }
361}
362
363/* Stop thread scheduler. */
364void
365thread_master_free (struct thread_master *m)
366{
367 thread_list_free (m, &m->read);
368 thread_list_free (m, &m->write);
369 thread_list_free (m, &m->timer);
370 thread_list_free (m, &m->event);
371 thread_list_free (m, &m->ready);
372 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000373 thread_list_free (m, &m->background);
374
paul718e3742002-12-13 20:15:29 +0000375 XFREE (MTYPE_THREAD_MASTER, m);
376}
377
378/* Delete top of the list and return it. */
379static struct thread *
380thread_trim_head (struct thread_list *list)
381{
382 if (list->head)
383 return thread_list_delete (list, list->head);
384 return NULL;
385}
386
387/* Thread list is empty or not. */
388int
389thread_empty (struct thread_list *list)
390{
391 return list->head ? 0 : 1;
392}
393
394/* Return remain time in second. */
395unsigned long
396thread_timer_remain_second (struct thread *thread)
397{
ajs8b70d0b2005-04-28 01:31:13 +0000398 gettimeofday (&recent_time, NULL);
paul718e3742002-12-13 20:15:29 +0000399
ajs8b70d0b2005-04-28 01:31:13 +0000400 if (thread->u.sands.tv_sec - recent_time.tv_sec > 0)
401 return thread->u.sands.tv_sec - recent_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000402 else
403 return 0;
404}
405
paule04ab742003-01-17 23:47:00 +0000406/* Trim blankspace and "()"s */
407static char *
hasso8c328f12004-10-05 21:01:23 +0000408strip_funcname (const char *funcname)
paule04ab742003-01-17 23:47:00 +0000409{
410 char buff[100];
411 char tmp, *ret, *e, *b = buff;
412
413 strncpy(buff, funcname, sizeof(buff));
414 buff[ sizeof(buff) -1] = '\0';
415 e = buff +strlen(buff) -1;
416
417 /* Wont work for funcname == "Word (explanation)" */
418
419 while (*b == ' ' || *b == '(')
420 ++b;
421 while (*e == ' ' || *e == ')')
422 --e;
423 e++;
424
425 tmp = *e;
426 *e = '\0';
paul9d11a192004-10-31 16:19:24 +0000427 ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
paule04ab742003-01-17 23:47:00 +0000428 *e = tmp;
429
430 return ret;
431}
432
paul718e3742002-12-13 20:15:29 +0000433/* Get new thread. */
434static struct thread *
435thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000436 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000437{
438 struct thread *thread;
439
440 if (m->unuse.head)
paul2946f652003-03-27 23:48:24 +0000441 {
442 thread = thread_trim_head (&m->unuse);
443 if (thread->funcname)
paul9d11a192004-10-31 16:19:24 +0000444 XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
paul2946f652003-03-27 23:48:24 +0000445 }
paul718e3742002-12-13 20:15:29 +0000446 else
447 {
448 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
449 m->alloc++;
450 }
451 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000452 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000453 thread->master = m;
454 thread->func = func;
455 thread->arg = arg;
456
paule04ab742003-01-17 23:47:00 +0000457 thread->funcname = strip_funcname(funcname);
458
paul718e3742002-12-13 20:15:29 +0000459 return thread;
460}
461
462/* Add new read thread. */
463struct thread *
paule04ab742003-01-17 23:47:00 +0000464funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000465 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000466{
467 struct thread *thread;
468
469 assert (m != NULL);
470
471 if (FD_ISSET (fd, &m->readfd))
472 {
473 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
474 return NULL;
475 }
476
paule04ab742003-01-17 23:47:00 +0000477 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000478 FD_SET (fd, &m->readfd);
479 thread->u.fd = fd;
480 thread_list_add (&m->read, thread);
481
482 return thread;
483}
484
485/* Add new write thread. */
486struct thread *
paule04ab742003-01-17 23:47:00 +0000487funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000488 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000489{
490 struct thread *thread;
491
492 assert (m != NULL);
493
494 if (FD_ISSET (fd, &m->writefd))
495 {
496 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
497 return NULL;
498 }
499
paule04ab742003-01-17 23:47:00 +0000500 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000501 FD_SET (fd, &m->writefd);
502 thread->u.fd = fd;
503 thread_list_add (&m->write, thread);
504
505 return thread;
506}
507
paul98c91ac2004-10-05 14:57:50 +0000508static struct thread *
509funcname_thread_add_timer_timeval (struct thread_master *m,
510 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000511 int type,
paul98c91ac2004-10-05 14:57:50 +0000512 void *arg,
513 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000514 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000515{
paul718e3742002-12-13 20:15:29 +0000516 struct thread *thread;
ajs8b70d0b2005-04-28 01:31:13 +0000517 struct timeval alarm_time;
paula48b4e62005-04-22 00:43:47 +0000518 struct thread_list *list;
paul718e3742002-12-13 20:15:29 +0000519 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000520
521 assert (m != NULL);
522
ajs8b70d0b2005-04-28 01:31:13 +0000523 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000524 assert (time_relative);
525
ajs8b70d0b2005-04-28 01:31:13 +0000526 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000527 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000528
529 /* Do we need jitter here? */
ajs8b70d0b2005-04-28 01:31:13 +0000530 gettimeofday (&recent_time, NULL);
531 alarm_time.tv_sec = recent_time.tv_sec + time_relative->tv_sec;
532 alarm_time.tv_usec = recent_time.tv_usec + time_relative->tv_usec;
533 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000534
535 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000536 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000537 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
538 break;
539
540 if (tt)
paula48b4e62005-04-22 00:43:47 +0000541 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000542 else
paula48b4e62005-04-22 00:43:47 +0000543 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000544
545 return thread;
546}
547
paul98c91ac2004-10-05 14:57:50 +0000548
549/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000550struct thread *
paul98c91ac2004-10-05 14:57:50 +0000551funcname_thread_add_timer (struct thread_master *m,
552 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000553 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000554{
paul98c91ac2004-10-05 14:57:50 +0000555 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000556
557 assert (m != NULL);
558
paul9076fbd2004-10-11 09:40:58 +0000559 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000560 trel.tv_usec = 0;
561
paula48b4e62005-04-22 00:43:47 +0000562 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
563 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000564}
565
566/* Add timer event thread with "millisecond" resolution */
567struct thread *
568funcname_thread_add_timer_msec (struct thread_master *m,
569 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000570 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000571{
572 struct timeval trel;
573
574 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000575
ajsaf04bd72004-12-28 17:00:12 +0000576 trel.tv_sec = timer / 1000;
577 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000578
paula48b4e62005-04-22 00:43:47 +0000579 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
580 arg, &trel, funcname);
581}
582
583/* Add a background thread, with an optional millisec delay */
584struct thread *
585funcname_thread_add_background (struct thread_master *m,
586 int (*func) (struct thread *),
587 void *arg, long delay,
588 const char *funcname)
589{
590 struct timeval trel;
591
592 assert (m != NULL);
593
594 if (delay)
595 {
596 trel.tv_sec = delay / 1000;
597 trel.tv_usec = 1000*(delay % 1000);
598 }
599 else
600 {
601 trel.tv_sec = 0;
602 trel.tv_usec = 0;
603 }
604
605 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
606 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000607}
608
paul718e3742002-12-13 20:15:29 +0000609/* Add simple event thread. */
610struct thread *
paule04ab742003-01-17 23:47:00 +0000611funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000612 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000613{
614 struct thread *thread;
615
616 assert (m != NULL);
617
paule04ab742003-01-17 23:47:00 +0000618 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000619 thread->u.val = val;
620 thread_list_add (&m->event, thread);
621
622 return thread;
623}
624
625/* Cancel thread from scheduler. */
626void
627thread_cancel (struct thread *thread)
628{
paula48b4e62005-04-22 00:43:47 +0000629 struct thread_list *list;
630
paul718e3742002-12-13 20:15:29 +0000631 switch (thread->type)
632 {
633 case THREAD_READ:
634 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
635 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000636 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000637 break;
638 case THREAD_WRITE:
639 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
640 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000641 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000642 break;
643 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000644 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000645 break;
646 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000647 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000648 break;
649 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000650 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000651 break;
paula48b4e62005-04-22 00:43:47 +0000652 case THREAD_BACKGROUND:
653 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000654 break;
paul718e3742002-12-13 20:15:29 +0000655 default:
paula48b4e62005-04-22 00:43:47 +0000656 return;
paul718e3742002-12-13 20:15:29 +0000657 break;
658 }
paula48b4e62005-04-22 00:43:47 +0000659 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000660 thread->type = THREAD_UNUSED;
661 thread_add_unuse (thread->master, thread);
662}
663
664/* Delete all events which has argument value arg. */
665void
666thread_cancel_event (struct thread_master *m, void *arg)
667{
668 struct thread *thread;
669
670 thread = m->event.head;
671 while (thread)
672 {
673 struct thread *t;
674
675 t = thread;
676 thread = t->next;
677
678 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000679 {
680 thread_list_delete (&m->event, t);
681 t->type = THREAD_UNUSED;
682 thread_add_unuse (m, t);
683 }
paul718e3742002-12-13 20:15:29 +0000684 }
685}
686
paula48b4e62005-04-22 00:43:47 +0000687static struct timeval *
688thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000689{
paula48b4e62005-04-22 00:43:47 +0000690 if (tlist->head)
paul718e3742002-12-13 20:15:29 +0000691 {
ajs8b70d0b2005-04-28 01:31:13 +0000692 *timer_val = timeval_subtract (tlist->head->u.sands, recent_time);
paul718e3742002-12-13 20:15:29 +0000693 return timer_val;
694 }
695 return NULL;
696}
paul718e3742002-12-13 20:15:29 +0000697
698struct thread *
699thread_run (struct thread_master *m, struct thread *thread,
700 struct thread *fetch)
701{
702 *fetch = *thread;
703 thread->type = THREAD_UNUSED;
704 thread_add_unuse (m, thread);
705 return fetch;
706}
707
paula48b4e62005-04-22 00:43:47 +0000708static int
709thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000710{
711 struct thread *thread;
712 struct thread *next;
713 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000714
715 assert (list);
716
paul718e3742002-12-13 20:15:29 +0000717 for (thread = list->head; thread; thread = next)
718 {
719 next = thread->next;
720
721 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000722 {
723 assert (FD_ISSET (THREAD_FD (thread), mfdset));
724 FD_CLR(THREAD_FD (thread), mfdset);
725 thread_list_delete (list, thread);
726 thread_list_add (&thread->master->ready, thread);
727 thread->type = THREAD_READY;
728 ready++;
729 }
paul718e3742002-12-13 20:15:29 +0000730 }
731 return ready;
732}
733
ajs8b70d0b2005-04-28 01:31:13 +0000734/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000735static unsigned int
736thread_timer_process (struct thread_list *list, struct timeval *timenow)
737{
738 struct thread *thread;
739 unsigned int ready = 0;
740
paula48b4e62005-04-22 00:43:47 +0000741 for (thread = list->head; thread; thread = thread->next)
ajs8b70d0b2005-04-28 01:31:13 +0000742 {
743 if (timeval_cmp (*timenow, thread->u.sands) < 0)
744 return ready;
745 thread_list_delete (list, thread);
746 thread->type = THREAD_READY;
747 thread_list_add (&thread->master->ready, thread);
748 ready++;
749 }
paula48b4e62005-04-22 00:43:47 +0000750 return ready;
751}
752
paul718e3742002-12-13 20:15:29 +0000753/* Fetch next ready thread. */
754struct thread *
755thread_fetch (struct thread_master *m, struct thread *fetch)
756{
paul718e3742002-12-13 20:15:29 +0000757 struct thread *thread;
758 fd_set readfd;
759 fd_set writefd;
760 fd_set exceptfd;
paul718e3742002-12-13 20:15:29 +0000761 struct timeval timer_val;
paula48b4e62005-04-22 00:43:47 +0000762 struct timeval timer_val_bg;
paul718e3742002-12-13 20:15:29 +0000763 struct timeval *timer_wait;
paula48b4e62005-04-22 00:43:47 +0000764 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +0000765
766 while (1)
767 {
paula48b4e62005-04-22 00:43:47 +0000768 int num = 0;
paula48b4e62005-04-22 00:43:47 +0000769
paul05c447d2004-07-22 19:14:27 +0000770 /* Signals are highest priority */
771 quagga_sigevent_process ();
772
773 /* Normal event are the next highest priority. */
paul718e3742002-12-13 20:15:29 +0000774 if ((thread = thread_trim_head (&m->event)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000775 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000776
paula48b4e62005-04-22 00:43:47 +0000777 /* If there are any ready threads from previous scheduler runs,
778 * process top of them.
779 */
paul718e3742002-12-13 20:15:29 +0000780 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000781 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000782
paul718e3742002-12-13 20:15:29 +0000783 /* Structure copy. */
784 readfd = m->readfd;
785 writefd = m->writefd;
786 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +0000787
788 /* Calculate select wait timer if nothing else to do */
ajs8b70d0b2005-04-28 01:31:13 +0000789 gettimeofday (&recent_time, NULL);
790 timer_wait = thread_timer_wait (&m->timer, &timer_val);
791 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
792
793 if (timer_wait_bg &&
794 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
795 timer_wait = timer_wait_bg;
paula48b4e62005-04-22 00:43:47 +0000796
paul718e3742002-12-13 20:15:29 +0000797 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +0000798
799 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +0000800 if (num < 0)
paul05c447d2004-07-22 19:14:27 +0000801 {
802 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +0000803 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +0000804 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +0000805 return NULL;
806 }
ajs8b70d0b2005-04-28 01:31:13 +0000807
808 /* Check foreground timers. Historically, they have had higher
809 priority than I/O threads, so let's push them onto the ready
810 list in front of the I/O threads. */
811 gettimeofday (&recent_time, NULL);
812 thread_timer_process (&m->timer, &recent_time);
paula48b4e62005-04-22 00:43:47 +0000813
814 /* Got IO, process it */
815 if (num > 0)
816 {
817 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000818 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +0000819 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000820 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +0000821 }
ajs8b70d0b2005-04-28 01:31:13 +0000822
823#if 0
824 /* If any threads were made ready above (I/O or foreground timer),
825 perhaps we should avoid adding background timers to the ready
826 list at this time. If this is code is uncommented, then background
827 timer threads will not run unless there is nothing else to do. */
828 if ((thread = thread_trim_head (&m->ready)) != NULL)
829 return thread_run (m, thread, fetch);
830#endif
831
paula48b4e62005-04-22 00:43:47 +0000832 /* Background timer/events, lowest priority */
ajs8b70d0b2005-04-28 01:31:13 +0000833 thread_timer_process (&m->background, &recent_time);
paula48b4e62005-04-22 00:43:47 +0000834
ajs8b70d0b2005-04-28 01:31:13 +0000835 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000836 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +0000837 }
838}
839
ajs924b9222005-04-16 17:11:24 +0000840unsigned long
ajs8b70d0b2005-04-28 01:31:13 +0000841thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +0000842{
paul718e3742002-12-13 20:15:29 +0000843#ifdef HAVE_RUSAGE
844 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +0000845 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
846 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +0000847#else
ajs8b70d0b2005-04-28 01:31:13 +0000848 *cputime = 0;
paul718e3742002-12-13 20:15:29 +0000849#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +0000850 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +0000851}
852
ajs8b70d0b2005-04-28 01:31:13 +0000853/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
854 Note: we are using real (wall clock) time for this calculation.
855 It could be argued that CPU time may make more sense in certain
856 contexts. The things to consider are whether the thread may have
857 blocked (in which case wall time increases, but CPU time does not),
858 or whether the system is heavily loaded with other processes competing
859 for CPU time. On balance, wall clock time seems to make sense.
860 Plus it has the added benefit that gettimeofday should be faster
861 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +0000862int
863thread_should_yield (struct thread *thread)
864{
ajs8b70d0b2005-04-28 01:31:13 +0000865 gettimeofday(&recent_time, NULL);
866 return (timeval_elapsed(recent_time, thread->ru.real) >
867 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +0000868}
869
870/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +0000871 use that to get in-depth stats on the performance of the thread in addition
872 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +0000873void
874thread_call (struct thread *thread)
875{
ajs8b70d0b2005-04-28 01:31:13 +0000876 unsigned long realtime, cputime;
paul718e3742002-12-13 20:15:29 +0000877 RUSAGE_T ru;
paule04ab742003-01-17 23:47:00 +0000878 struct cpu_thread_history tmp, *cpu;
879
880 tmp.func = thread->func;
881 tmp.funcname = thread->funcname;
882 cpu = hash_get(cpu_record, &tmp, cpu_record_hash_alloc);
paul718e3742002-12-13 20:15:29 +0000883
884 GETRUSAGE (&thread->ru);
885
886 (*thread->func) (thread);
887
888 GETRUSAGE (&ru);
889
ajs8b70d0b2005-04-28 01:31:13 +0000890 realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
891 cpu->real.total += realtime;
892 if (cpu->real.max < realtime)
893 cpu->real.max = realtime;
894#ifdef HAVE_RUSAGE
895 cpu->cpu.total += cputime;
896 if (cpu->cpu.max < cputime)
897 cpu->cpu.max = cputime;
898#endif
paule04ab742003-01-17 23:47:00 +0000899
900 ++cpu->total_calls;
901 cpu->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +0000902
ajs924b9222005-04-16 17:11:24 +0000903#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +0000904 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +0000905 {
906 /*
907 * We have a CPU Hog on our hands.
908 * Whinge about it now, so we're aware this is yet another task
909 * to fix.
910 */
ajs8b70d0b2005-04-28 01:31:13 +0000911 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +0000912 thread->funcname,
913 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +0000914 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +0000915 }
ajs924b9222005-04-16 17:11:24 +0000916#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +0000917}
918
919/* Execute thread */
920struct thread *
paule04ab742003-01-17 23:47:00 +0000921funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +0000922 int (*func)(struct thread *),
923 void *arg,
paule04ab742003-01-17 23:47:00 +0000924 int val,
hasso8c328f12004-10-05 21:01:23 +0000925 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000926{
927 struct thread dummy;
928
929 memset (&dummy, 0, sizeof (struct thread));
930
931 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +0000932 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +0000933 dummy.master = NULL;
934 dummy.func = func;
935 dummy.arg = arg;
936 dummy.u.val = val;
paule04ab742003-01-17 23:47:00 +0000937 dummy.funcname = strip_funcname (funcname);
paul718e3742002-12-13 20:15:29 +0000938 thread_call (&dummy);
939
paul9d11a192004-10-31 16:19:24 +0000940 XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
paul2946f652003-03-27 23:48:24 +0000941
paul718e3742002-12-13 20:15:29 +0000942 return NULL;
943}