blob: fd841c2153998ef386a4e0c4ceb952fdcf1f69e4 [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
Paul Jakmadb9c0df2006-08-27 06:44:02 +000033/* Recent absolute time of day */
ajs8b70d0b2005-04-28 01:31:13 +000034struct timeval recent_time;
Paul Jakmadb9c0df2006-08-27 06:44:02 +000035static struct timeval last_recent_time;
36/* Relative time, since startup */
37static struct timeval relative_time;
38static struct timeval relative_time_base;
39/* init flag */
40static unsigned short timers_inited;
41
paule04ab742003-01-17 23:47:00 +000042static struct hash *cpu_record = NULL;
paul718e3742002-12-13 20:15:29 +000043
44/* Struct timeval's tv_usec one second value. */
45#define TIMER_SECOND_MICRO 1000000L
46
ajs8b70d0b2005-04-28 01:31:13 +000047/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
48 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000049static struct timeval
paul718e3742002-12-13 20:15:29 +000050timeval_adjust (struct timeval a)
51{
52 while (a.tv_usec >= TIMER_SECOND_MICRO)
53 {
54 a.tv_usec -= TIMER_SECOND_MICRO;
55 a.tv_sec++;
56 }
57
58 while (a.tv_usec < 0)
59 {
60 a.tv_usec += TIMER_SECOND_MICRO;
61 a.tv_sec--;
62 }
63
64 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000065 /* Change negative timeouts to 0. */
66 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000067
68 return a;
69}
70
71static struct timeval
72timeval_subtract (struct timeval a, struct timeval b)
73{
74 struct timeval ret;
75
76 ret.tv_usec = a.tv_usec - b.tv_usec;
77 ret.tv_sec = a.tv_sec - b.tv_sec;
78
79 return timeval_adjust (ret);
80}
81
ajs8b70d0b2005-04-28 01:31:13 +000082static long
paul718e3742002-12-13 20:15:29 +000083timeval_cmp (struct timeval a, struct timeval b)
84{
85 return (a.tv_sec == b.tv_sec
86 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
87}
88
89static unsigned long
90timeval_elapsed (struct timeval a, struct timeval b)
91{
92 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
93 + (a.tv_usec - b.tv_usec));
94}
95
Paul Jakmadb9c0df2006-08-27 06:44:02 +000096#ifndef HAVE_CLOCK_MONOTONIC
97static void
98quagga_gettimeofday_relative_adjust (void)
99{
100 struct timeval diff;
101 if (timeval_cmp (recent_time, last_recent_time) < 0)
102 {
103 relative_time.tv_sec++;
104 relative_time.tv_usec = 0;
105 }
106 else
107 {
108 diff = timeval_subtract (recent_time, last_recent_time);
109 relative_time.tv_sec += diff.tv_sec;
110 relative_time.tv_usec += diff.tv_usec;
111 relative_time = timeval_adjust (relative_time);
112 }
113 last_recent_time = recent_time;
114}
115#endif /* !HAVE_CLOCK_MONOTONIC */
116
117/* gettimeofday wrapper, to keep recent_time updated */
118static int
119quagga_gettimeofday (struct timeval *tv)
120{
121 int ret;
122
123 assert (tv);
124
125 if (!(ret = gettimeofday (&recent_time, NULL)))
126 {
127 /* init... */
128 if (!timers_inited)
129 {
130 relative_time_base = last_recent_time = recent_time;
131 timers_inited = 1;
132 }
133 /* avoid copy if user passed recent_time pointer.. */
134 if (tv != &recent_time)
135 *tv = recent_time;
136 return 0;
137 }
138 return ret;
139}
140
141static int
142quagga_get_relative (struct timeval *tv)
143{
144 int ret;
145
146#ifdef HAVE_CLOCK_MONOTONIC
147 {
148 struct timespec tp;
149 if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
150 {
151 relative_time.tv_sec = tp.tv_sec;
152 relative_time.tv_usec = tp.tv_nsec / 1000;
153 }
154 }
155#else /* !HAVE_CLOCK_MONOTONIC */
156 if (!(ret = quagga_gettimeofday (&recent_time)))
157 quagga_gettimeofday_relative_adjust();
158#endif /* HAVE_CLOCK_MONOTONIC */
159
160 if (tv)
161 *tv = relative_time;
162
163 return ret;
164}
165
166/* Get absolute time stamp, but in terms of the internal timer
167 * Could be wrong, but at least won't go back.
168 */
169static void
170quagga_real_stabilised (struct timeval *tv)
171{
172 *tv = relative_time_base;
173 tv->tv_sec += relative_time.tv_sec;
174 tv->tv_usec += relative_time.tv_usec;
175 *tv = timeval_adjust (*tv);
176}
177
178/* Exported Quagga timestamp function.
179 * Modelled on POSIX clock_gettime.
180 */
181int
182quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
183{
184 switch (clkid)
185 {
186 case QUAGGA_CLK_REALTIME:
187 return quagga_gettimeofday (tv);
188 case QUAGGA_CLK_MONOTONIC:
189 return quagga_get_relative (tv);
190 case QUAGGA_CLK_REALTIME_STABILISED:
191 quagga_real_stabilised (tv);
192 return 0;
193 default:
194 errno = EINVAL;
195 return -1;
196 }
197}
198
199/* time_t value in terms of stabilised absolute time.
200 * replacement for POSIX time()
201 */
202time_t
203quagga_time (time_t *t)
204{
205 struct timeval tv;
206 quagga_real_stabilised (&tv);
207 if (t)
208 *t = tv.tv_sec;
209 return tv.tv_sec;
210}
211
212/* Public export of recent_relative_time by value */
213struct timeval
214recent_relative_time (void)
215{
216 return relative_time;
217}
218
paula48b4e62005-04-22 00:43:47 +0000219static unsigned int
paule04ab742003-01-17 23:47:00 +0000220cpu_record_hash_key (struct cpu_thread_history *a)
221{
paul8cc41982005-05-06 21:25:49 +0000222 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +0000223}
224
225static int
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100226cpu_record_hash_cmp (const struct cpu_thread_history *a,
227 const struct cpu_thread_history *b)
paule04ab742003-01-17 23:47:00 +0000228{
229 return a->func == b->func;
230}
231
paul8cc41982005-05-06 21:25:49 +0000232static void *
paule04ab742003-01-17 23:47:00 +0000233cpu_record_hash_alloc (struct cpu_thread_history *a)
234{
235 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000236 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000237 new->func = a->func;
paul9d11a192004-10-31 16:19:24 +0000238 new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname);
paule04ab742003-01-17 23:47:00 +0000239 return new;
240}
241
Chris Caputo228da422009-07-18 05:44:03 +0000242static void
243cpu_record_hash_free (void *a)
244{
245 struct cpu_thread_history *hist = a;
246
247 XFREE (MTYPE_THREAD_FUNCNAME, hist->funcname);
248 XFREE (MTYPE_THREAD_STATS, hist);
249}
250
paule04ab742003-01-17 23:47:00 +0000251static inline void
252vty_out_cpu_thread_history(struct vty* vty,
253 struct cpu_thread_history *a)
254{
ajs8b70d0b2005-04-28 01:31:13 +0000255#ifdef HAVE_RUSAGE
256 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
257 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
258 a->cpu.total/a->total_calls, a->cpu.max,
259 a->real.total/a->total_calls, a->real.max);
260#else
261 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
262 a->real.total/1000, a->real.total%1000, a->total_calls,
263 a->real.total/a->total_calls, a->real.max);
264#endif
265 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000266 a->types & (1 << THREAD_READ) ? 'R':' ',
267 a->types & (1 << THREAD_WRITE) ? 'W':' ',
268 a->types & (1 << THREAD_TIMER) ? 'T':' ',
269 a->types & (1 << THREAD_EVENT) ? 'E':' ',
270 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000271 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000272 a->funcname, VTY_NEWLINE);
273}
274
275static void
276cpu_record_hash_print(struct hash_backet *bucket,
277 void *args[])
278{
279 struct cpu_thread_history *totals = args[0];
280 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100281 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000282 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000283
paule04ab742003-01-17 23:47:00 +0000284 a = bucket->data;
285 if ( !(a->types & *filter) )
286 return;
287 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000288 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000289 totals->real.total += a->real.total;
290 if (totals->real.max < a->real.max)
291 totals->real.max = a->real.max;
292#ifdef HAVE_RUSAGE
293 totals->cpu.total += a->cpu.total;
294 if (totals->cpu.max < a->cpu.max)
295 totals->cpu.max = a->cpu.max;
296#endif
paule04ab742003-01-17 23:47:00 +0000297}
298
299static void
Paul Jakma41b23732009-06-30 16:12:49 +0100300cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000301{
302 struct cpu_thread_history tmp;
303 void *args[3] = {&tmp, vty, &filter};
304
305 memset(&tmp, 0, sizeof tmp);
Paul Jakma85261002010-01-11 16:30:45 +0000306 tmp.funcname = (char *)"TOTAL";
paule04ab742003-01-17 23:47:00 +0000307 tmp.types = filter;
308
ajs8b70d0b2005-04-28 01:31:13 +0000309#ifdef HAVE_RUSAGE
310 vty_out(vty, "%21s %18s %18s%s",
311 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
312#endif
313 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
314#ifdef HAVE_RUSAGE
315 vty_out(vty, " Avg uSec Max uSecs");
316#endif
317 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000318 hash_iterate(cpu_record,
319 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
320 args);
321
322 if (tmp.total_calls > 0)
323 vty_out_cpu_thread_history(vty, &tmp);
324}
325
326DEFUN(show_thread_cpu,
327 show_thread_cpu_cmd,
328 "show thread cpu [FILTER]",
329 SHOW_STR
330 "Thread information\n"
331 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000332 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000333{
334 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100335 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000336
337 if (argc > 0)
338 {
339 filter = 0;
340 while (argv[0][i] != '\0')
341 {
342 switch ( argv[0][i] )
343 {
344 case 'r':
345 case 'R':
346 filter |= (1 << THREAD_READ);
347 break;
348 case 'w':
349 case 'W':
350 filter |= (1 << THREAD_WRITE);
351 break;
352 case 't':
353 case 'T':
354 filter |= (1 << THREAD_TIMER);
355 break;
356 case 'e':
357 case 'E':
358 filter |= (1 << THREAD_EVENT);
359 break;
360 case 'x':
361 case 'X':
362 filter |= (1 << THREAD_EXECUTE);
363 break;
paula48b4e62005-04-22 00:43:47 +0000364 case 'b':
365 case 'B':
366 filter |= (1 << THREAD_BACKGROUND);
367 break;
paule04ab742003-01-17 23:47:00 +0000368 default:
369 break;
370 }
371 ++i;
372 }
373 if (filter == 0)
374 {
paula48b4e62005-04-22 00:43:47 +0000375 vty_out(vty, "Invalid filter \"%s\" specified,"
376 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000377 argv[0], VTY_NEWLINE);
378 return CMD_WARNING;
379 }
380 }
381
382 cpu_record_print(vty, filter);
383 return CMD_SUCCESS;
384}
Paul Jakmae276eb82010-01-09 16:15:00 +0000385
386static void
387cpu_record_hash_clear (struct hash_backet *bucket,
388 void *args)
389{
390 thread_type *filter = args;
391 struct cpu_thread_history *a = bucket->data;
392
393 a = bucket->data;
394 if ( !(a->types & *filter) )
395 return;
396
397 hash_release (cpu_record, bucket->data);
398}
399
400static void
401cpu_record_clear (thread_type filter)
402{
403 thread_type *tmp = &filter;
404 hash_iterate (cpu_record,
405 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
406 tmp);
407}
408
409DEFUN(clear_thread_cpu,
410 clear_thread_cpu_cmd,
411 "clear thread cpu [FILTER]",
412 "Clear stored data\n"
413 "Thread information\n"
414 "Thread CPU usage\n"
415 "Display filter (rwtexb)\n")
416{
417 int i = 0;
418 thread_type filter = (thread_type) -1U;
419
420 if (argc > 0)
421 {
422 filter = 0;
423 while (argv[0][i] != '\0')
424 {
425 switch ( argv[0][i] )
426 {
427 case 'r':
428 case 'R':
429 filter |= (1 << THREAD_READ);
430 break;
431 case 'w':
432 case 'W':
433 filter |= (1 << THREAD_WRITE);
434 break;
435 case 't':
436 case 'T':
437 filter |= (1 << THREAD_TIMER);
438 break;
439 case 'e':
440 case 'E':
441 filter |= (1 << THREAD_EVENT);
442 break;
443 case 'x':
444 case 'X':
445 filter |= (1 << THREAD_EXECUTE);
446 break;
447 case 'b':
448 case 'B':
449 filter |= (1 << THREAD_BACKGROUND);
450 break;
451 default:
452 break;
453 }
454 ++i;
455 }
456 if (filter == 0)
457 {
458 vty_out(vty, "Invalid filter \"%s\" specified,"
459 " must contain at least one of 'RWTEXB'%s",
460 argv[0], VTY_NEWLINE);
461 return CMD_WARNING;
462 }
463 }
464
465 cpu_record_clear (filter);
466 return CMD_SUCCESS;
467}
paule04ab742003-01-17 23:47:00 +0000468
paul718e3742002-12-13 20:15:29 +0000469/* List allocation and head/tail print out. */
470static void
471thread_list_debug (struct thread_list *list)
472{
473 printf ("count [%d] head [%p] tail [%p]\n",
474 list->count, list->head, list->tail);
475}
476
477/* Debug print for thread_master. */
paul8cc41982005-05-06 21:25:49 +0000478static void __attribute__ ((unused))
paul718e3742002-12-13 20:15:29 +0000479thread_master_debug (struct thread_master *m)
480{
481 printf ("-----------\n");
482 printf ("readlist : ");
483 thread_list_debug (&m->read);
484 printf ("writelist : ");
485 thread_list_debug (&m->write);
486 printf ("timerlist : ");
487 thread_list_debug (&m->timer);
488 printf ("eventlist : ");
489 thread_list_debug (&m->event);
490 printf ("unuselist : ");
491 thread_list_debug (&m->unuse);
paula48b4e62005-04-22 00:43:47 +0000492 printf ("bgndlist : ");
493 thread_list_debug (&m->background);
paul718e3742002-12-13 20:15:29 +0000494 printf ("total alloc: [%ld]\n", m->alloc);
495 printf ("-----------\n");
496}
497
498/* Allocate new thread master. */
499struct thread_master *
500thread_master_create ()
501{
paule04ab742003-01-17 23:47:00 +0000502 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000503 cpu_record
504 = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100505 (int (*) (const void *, const void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000506
paul718e3742002-12-13 20:15:29 +0000507 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
508 sizeof (struct thread_master));
509}
510
511/* Add a new thread to the list. */
512static void
513thread_list_add (struct thread_list *list, struct thread *thread)
514{
515 thread->next = NULL;
516 thread->prev = list->tail;
517 if (list->tail)
518 list->tail->next = thread;
519 else
520 list->head = thread;
521 list->tail = thread;
522 list->count++;
523}
524
525/* Add a new thread just before the point. */
526static void
527thread_list_add_before (struct thread_list *list,
528 struct thread *point,
529 struct thread *thread)
530{
531 thread->next = point;
532 thread->prev = point->prev;
533 if (point->prev)
534 point->prev->next = thread;
535 else
536 list->head = thread;
537 point->prev = thread;
538 list->count++;
539}
540
541/* Delete a thread from the list. */
542static struct thread *
543thread_list_delete (struct thread_list *list, struct thread *thread)
544{
545 if (thread->next)
546 thread->next->prev = thread->prev;
547 else
548 list->tail = thread->prev;
549 if (thread->prev)
550 thread->prev->next = thread->next;
551 else
552 list->head = thread->next;
553 thread->next = thread->prev = NULL;
554 list->count--;
555 return thread;
556}
557
558/* Move thread to unuse list. */
559static void
560thread_add_unuse (struct thread_master *m, struct thread *thread)
561{
paula48b4e62005-04-22 00:43:47 +0000562 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000563 assert (thread->next == NULL);
564 assert (thread->prev == NULL);
565 assert (thread->type == THREAD_UNUSED);
566 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000567 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000568}
569
570/* Free all unused thread. */
571static void
572thread_list_free (struct thread_master *m, struct thread_list *list)
573{
574 struct thread *t;
575 struct thread *next;
576
577 for (t = list->head; t; t = next)
578 {
579 next = t->next;
Chris Caputo228da422009-07-18 05:44:03 +0000580 if (t->funcname)
581 XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
paul718e3742002-12-13 20:15:29 +0000582 XFREE (MTYPE_THREAD, t);
583 list->count--;
584 m->alloc--;
585 }
586}
587
588/* Stop thread scheduler. */
589void
590thread_master_free (struct thread_master *m)
591{
592 thread_list_free (m, &m->read);
593 thread_list_free (m, &m->write);
594 thread_list_free (m, &m->timer);
595 thread_list_free (m, &m->event);
596 thread_list_free (m, &m->ready);
597 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000598 thread_list_free (m, &m->background);
599
paul718e3742002-12-13 20:15:29 +0000600 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000601
602 if (cpu_record)
603 {
604 hash_clean (cpu_record, cpu_record_hash_free);
605 hash_free (cpu_record);
606 cpu_record = NULL;
607 }
paul718e3742002-12-13 20:15:29 +0000608}
609
paul8cc41982005-05-06 21:25:49 +0000610/* Thread list is empty or not. */
611static inline int
612thread_empty (struct thread_list *list)
613{
614 return list->head ? 0 : 1;
615}
616
paul718e3742002-12-13 20:15:29 +0000617/* Delete top of the list and return it. */
618static struct thread *
619thread_trim_head (struct thread_list *list)
620{
paul8cc41982005-05-06 21:25:49 +0000621 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000622 return thread_list_delete (list, list->head);
623 return NULL;
624}
625
paul718e3742002-12-13 20:15:29 +0000626/* Return remain time in second. */
627unsigned long
628thread_timer_remain_second (struct thread *thread)
629{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000630 quagga_get_relative (NULL);
631
632 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
633 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000634 else
635 return 0;
636}
637
paule04ab742003-01-17 23:47:00 +0000638/* Trim blankspace and "()"s */
639static char *
hasso8c328f12004-10-05 21:01:23 +0000640strip_funcname (const char *funcname)
paule04ab742003-01-17 23:47:00 +0000641{
642 char buff[100];
643 char tmp, *ret, *e, *b = buff;
644
645 strncpy(buff, funcname, sizeof(buff));
646 buff[ sizeof(buff) -1] = '\0';
647 e = buff +strlen(buff) -1;
648
649 /* Wont work for funcname == "Word (explanation)" */
650
651 while (*b == ' ' || *b == '(')
652 ++b;
653 while (*e == ' ' || *e == ')')
654 --e;
655 e++;
656
657 tmp = *e;
658 *e = '\0';
paul9d11a192004-10-31 16:19:24 +0000659 ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
paule04ab742003-01-17 23:47:00 +0000660 *e = tmp;
661
662 return ret;
663}
664
paul718e3742002-12-13 20:15:29 +0000665/* Get new thread. */
666static struct thread *
667thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000668 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000669{
670 struct thread *thread;
671
paul8cc41982005-05-06 21:25:49 +0000672 if (!thread_empty (&m->unuse))
paul2946f652003-03-27 23:48:24 +0000673 {
674 thread = thread_trim_head (&m->unuse);
675 if (thread->funcname)
paul9d11a192004-10-31 16:19:24 +0000676 XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
paul2946f652003-03-27 23:48:24 +0000677 }
paul718e3742002-12-13 20:15:29 +0000678 else
679 {
680 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
681 m->alloc++;
682 }
683 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000684 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000685 thread->master = m;
686 thread->func = func;
687 thread->arg = arg;
688
paule04ab742003-01-17 23:47:00 +0000689 thread->funcname = strip_funcname(funcname);
690
paul718e3742002-12-13 20:15:29 +0000691 return thread;
692}
693
694/* Add new read thread. */
695struct thread *
paule04ab742003-01-17 23:47:00 +0000696funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000697 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000698{
699 struct thread *thread;
700
701 assert (m != NULL);
702
703 if (FD_ISSET (fd, &m->readfd))
704 {
705 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
706 return NULL;
707 }
708
paule04ab742003-01-17 23:47:00 +0000709 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000710 FD_SET (fd, &m->readfd);
711 thread->u.fd = fd;
712 thread_list_add (&m->read, thread);
713
714 return thread;
715}
716
717/* Add new write thread. */
718struct thread *
paule04ab742003-01-17 23:47:00 +0000719funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000720 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000721{
722 struct thread *thread;
723
724 assert (m != NULL);
725
726 if (FD_ISSET (fd, &m->writefd))
727 {
728 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
729 return NULL;
730 }
731
paule04ab742003-01-17 23:47:00 +0000732 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000733 FD_SET (fd, &m->writefd);
734 thread->u.fd = fd;
735 thread_list_add (&m->write, thread);
736
737 return thread;
738}
739
paul98c91ac2004-10-05 14:57:50 +0000740static struct thread *
741funcname_thread_add_timer_timeval (struct thread_master *m,
742 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000743 int type,
paul98c91ac2004-10-05 14:57:50 +0000744 void *arg,
745 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000746 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000747{
paul718e3742002-12-13 20:15:29 +0000748 struct thread *thread;
paula48b4e62005-04-22 00:43:47 +0000749 struct thread_list *list;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000750 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000751 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000752
753 assert (m != NULL);
754
ajs8b70d0b2005-04-28 01:31:13 +0000755 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000756 assert (time_relative);
757
ajs8b70d0b2005-04-28 01:31:13 +0000758 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000759 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000760
761 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100762 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000763 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
764 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000765 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000766
767 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000768 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000769 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
770 break;
771
772 if (tt)
paula48b4e62005-04-22 00:43:47 +0000773 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000774 else
paula48b4e62005-04-22 00:43:47 +0000775 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000776
777 return thread;
778}
779
paul98c91ac2004-10-05 14:57:50 +0000780
781/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000782struct thread *
paul98c91ac2004-10-05 14:57:50 +0000783funcname_thread_add_timer (struct thread_master *m,
784 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000785 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000786{
paul98c91ac2004-10-05 14:57:50 +0000787 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000788
789 assert (m != NULL);
790
paul9076fbd2004-10-11 09:40:58 +0000791 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000792 trel.tv_usec = 0;
793
paula48b4e62005-04-22 00:43:47 +0000794 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
795 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000796}
797
798/* Add timer event thread with "millisecond" resolution */
799struct thread *
800funcname_thread_add_timer_msec (struct thread_master *m,
801 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000802 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000803{
804 struct timeval trel;
805
806 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000807
ajsaf04bd72004-12-28 17:00:12 +0000808 trel.tv_sec = timer / 1000;
809 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000810
paula48b4e62005-04-22 00:43:47 +0000811 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
812 arg, &trel, funcname);
813}
814
815/* Add a background thread, with an optional millisec delay */
816struct thread *
817funcname_thread_add_background (struct thread_master *m,
818 int (*func) (struct thread *),
819 void *arg, long delay,
820 const char *funcname)
821{
822 struct timeval trel;
823
824 assert (m != NULL);
825
826 if (delay)
827 {
828 trel.tv_sec = delay / 1000;
829 trel.tv_usec = 1000*(delay % 1000);
830 }
831 else
832 {
833 trel.tv_sec = 0;
834 trel.tv_usec = 0;
835 }
836
837 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
838 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000839}
840
paul718e3742002-12-13 20:15:29 +0000841/* Add simple event thread. */
842struct thread *
paule04ab742003-01-17 23:47:00 +0000843funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000844 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000845{
846 struct thread *thread;
847
848 assert (m != NULL);
849
paule04ab742003-01-17 23:47:00 +0000850 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000851 thread->u.val = val;
852 thread_list_add (&m->event, thread);
853
854 return thread;
855}
856
857/* Cancel thread from scheduler. */
858void
859thread_cancel (struct thread *thread)
860{
paula48b4e62005-04-22 00:43:47 +0000861 struct thread_list *list;
862
paul718e3742002-12-13 20:15:29 +0000863 switch (thread->type)
864 {
865 case THREAD_READ:
866 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
867 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000868 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000869 break;
870 case THREAD_WRITE:
871 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
872 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000873 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000874 break;
875 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000876 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000877 break;
878 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000879 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000880 break;
881 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000882 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000883 break;
paula48b4e62005-04-22 00:43:47 +0000884 case THREAD_BACKGROUND:
885 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000886 break;
paul718e3742002-12-13 20:15:29 +0000887 default:
paula48b4e62005-04-22 00:43:47 +0000888 return;
paul718e3742002-12-13 20:15:29 +0000889 break;
890 }
paula48b4e62005-04-22 00:43:47 +0000891 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000892 thread->type = THREAD_UNUSED;
893 thread_add_unuse (thread->master, thread);
894}
895
896/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000897unsigned int
paul718e3742002-12-13 20:15:29 +0000898thread_cancel_event (struct thread_master *m, void *arg)
899{
pauldc818072005-05-19 01:30:53 +0000900 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000901 struct thread *thread;
902
903 thread = m->event.head;
904 while (thread)
905 {
906 struct thread *t;
907
908 t = thread;
909 thread = t->next;
910
911 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000912 {
pauldc818072005-05-19 01:30:53 +0000913 ret++;
paula48b4e62005-04-22 00:43:47 +0000914 thread_list_delete (&m->event, t);
915 t->type = THREAD_UNUSED;
916 thread_add_unuse (m, t);
917 }
paul718e3742002-12-13 20:15:29 +0000918 }
pauldc818072005-05-19 01:30:53 +0000919 return ret;
paul718e3742002-12-13 20:15:29 +0000920}
921
paula48b4e62005-04-22 00:43:47 +0000922static struct timeval *
923thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000924{
paul8cc41982005-05-06 21:25:49 +0000925 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000926 {
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000927 *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000928 return timer_val;
929 }
930 return NULL;
931}
paul718e3742002-12-13 20:15:29 +0000932
paul8cc41982005-05-06 21:25:49 +0000933static struct thread *
paul718e3742002-12-13 20:15:29 +0000934thread_run (struct thread_master *m, struct thread *thread,
935 struct thread *fetch)
936{
937 *fetch = *thread;
938 thread->type = THREAD_UNUSED;
Chris Caputo228da422009-07-18 05:44:03 +0000939 thread->funcname = NULL; /* thread_call will free fetch's copied pointer */
paul718e3742002-12-13 20:15:29 +0000940 thread_add_unuse (m, thread);
941 return fetch;
942}
943
paula48b4e62005-04-22 00:43:47 +0000944static int
945thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000946{
947 struct thread *thread;
948 struct thread *next;
949 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000950
951 assert (list);
952
paul718e3742002-12-13 20:15:29 +0000953 for (thread = list->head; thread; thread = next)
954 {
955 next = thread->next;
956
957 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000958 {
959 assert (FD_ISSET (THREAD_FD (thread), mfdset));
960 FD_CLR(THREAD_FD (thread), mfdset);
961 thread_list_delete (list, thread);
962 thread_list_add (&thread->master->ready, thread);
963 thread->type = THREAD_READY;
964 ready++;
965 }
paul718e3742002-12-13 20:15:29 +0000966 }
967 return ready;
968}
969
ajs8b70d0b2005-04-28 01:31:13 +0000970/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000971static unsigned int
972thread_timer_process (struct thread_list *list, struct timeval *timenow)
973{
974 struct thread *thread;
975 unsigned int ready = 0;
976
paula48b4e62005-04-22 00:43:47 +0000977 for (thread = list->head; thread; thread = thread->next)
ajs8b70d0b2005-04-28 01:31:13 +0000978 {
979 if (timeval_cmp (*timenow, thread->u.sands) < 0)
980 return ready;
981 thread_list_delete (list, thread);
982 thread->type = THREAD_READY;
983 thread_list_add (&thread->master->ready, thread);
984 ready++;
985 }
paula48b4e62005-04-22 00:43:47 +0000986 return ready;
987}
988
Paul Jakma2613abe2010-01-11 16:33:07 +0000989/* process a list en masse, e.g. for event thread lists */
990static unsigned int
991thread_process (struct thread_list *list)
992{
993 struct thread *thread;
994 unsigned int ready = 0;
995
996 for (thread = list->head; thread; thread = thread->next)
997 {
998 thread_list_delete (list, thread);
999 thread->type = THREAD_READY;
1000 thread_list_add (&thread->master->ready, thread);
1001 ready++;
1002 }
1003 return ready;
1004}
1005
1006
paul718e3742002-12-13 20:15:29 +00001007/* Fetch next ready thread. */
1008struct thread *
1009thread_fetch (struct thread_master *m, struct thread *fetch)
1010{
paul718e3742002-12-13 20:15:29 +00001011 struct thread *thread;
1012 fd_set readfd;
1013 fd_set writefd;
1014 fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001015 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001016 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001017 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001018 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001019
1020 while (1)
1021 {
paula48b4e62005-04-22 00:43:47 +00001022 int num = 0;
paula48b4e62005-04-22 00:43:47 +00001023
Paul Jakma2613abe2010-01-11 16:33:07 +00001024 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001025 quagga_sigevent_process ();
1026
Paul Jakma2613abe2010-01-11 16:33:07 +00001027 /* Drain the ready queue of already scheduled jobs, before scheduling
1028 * more.
paula48b4e62005-04-22 00:43:47 +00001029 */
paul718e3742002-12-13 20:15:29 +00001030 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001031 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001032
Paul Jakma2613abe2010-01-11 16:33:07 +00001033 /* To be fair to all kinds of threads, and avoid starvation, we
1034 * need to be careful to consider all thread types for scheduling
1035 * in each quanta. I.e. we should not return early from here on.
1036 */
1037
1038 /* Normal event are the next highest priority. */
1039 thread_process (&m->event);
1040
paul718e3742002-12-13 20:15:29 +00001041 /* Structure copy. */
1042 readfd = m->readfd;
1043 writefd = m->writefd;
1044 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +00001045
1046 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001047 if (m->ready.count == 0)
1048 {
1049 quagga_get_relative (NULL);
1050 timer_wait = thread_timer_wait (&m->timer, &timer_val);
1051 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
1052
1053 if (timer_wait_bg &&
1054 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1055 timer_wait = timer_wait_bg;
1056 }
paula48b4e62005-04-22 00:43:47 +00001057
paul718e3742002-12-13 20:15:29 +00001058 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001059
1060 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001061 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001062 {
1063 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001064 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001065 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001066 return NULL;
1067 }
ajs8b70d0b2005-04-28 01:31:13 +00001068
1069 /* Check foreground timers. Historically, they have had higher
1070 priority than I/O threads, so let's push them onto the ready
1071 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001072 quagga_get_relative (NULL);
1073 thread_timer_process (&m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001074
1075 /* Got IO, process it */
1076 if (num > 0)
1077 {
1078 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001079 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +00001080 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001081 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +00001082 }
ajs8b70d0b2005-04-28 01:31:13 +00001083
1084#if 0
1085 /* If any threads were made ready above (I/O or foreground timer),
1086 perhaps we should avoid adding background timers to the ready
1087 list at this time. If this is code is uncommented, then background
1088 timer threads will not run unless there is nothing else to do. */
1089 if ((thread = thread_trim_head (&m->ready)) != NULL)
1090 return thread_run (m, thread, fetch);
1091#endif
1092
paula48b4e62005-04-22 00:43:47 +00001093 /* Background timer/events, lowest priority */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001094 thread_timer_process (&m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001095
ajs8b70d0b2005-04-28 01:31:13 +00001096 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001097 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001098 }
1099}
1100
ajs924b9222005-04-16 17:11:24 +00001101unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001102thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001103{
paul718e3742002-12-13 20:15:29 +00001104#ifdef HAVE_RUSAGE
1105 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001106 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1107 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001108#else
ajs8b70d0b2005-04-28 01:31:13 +00001109 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001110#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001111 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001112}
1113
ajs8b70d0b2005-04-28 01:31:13 +00001114/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1115 Note: we are using real (wall clock) time for this calculation.
1116 It could be argued that CPU time may make more sense in certain
1117 contexts. The things to consider are whether the thread may have
1118 blocked (in which case wall time increases, but CPU time does not),
1119 or whether the system is heavily loaded with other processes competing
1120 for CPU time. On balance, wall clock time seems to make sense.
1121 Plus it has the added benefit that gettimeofday should be faster
1122 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001123int
1124thread_should_yield (struct thread *thread)
1125{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001126 quagga_get_relative (NULL);
1127 return (timeval_elapsed(relative_time, thread->ru.real) >
ajs8b70d0b2005-04-28 01:31:13 +00001128 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001129}
1130
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001131void
1132thread_getrusage (RUSAGE_T *r)
1133{
1134 quagga_get_relative (NULL);
1135#ifdef HAVE_RUSAGE
1136 getrusage(RUSAGE_SELF, &(r->cpu));
1137#endif
1138 r->real = relative_time;
1139
1140#ifdef HAVE_CLOCK_MONOTONIC
1141 /* quagga_get_relative() only updates recent_time if gettimeofday
1142 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1143 * and guarantee to update it before threads are run...
1144 */
1145 quagga_gettimeofday(&recent_time);
1146#endif /* HAVE_CLOCK_MONOTONIC */
1147}
1148
paul718e3742002-12-13 20:15:29 +00001149/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001150 use that to get in-depth stats on the performance of the thread in addition
1151 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001152void
1153thread_call (struct thread *thread)
1154{
ajs8b70d0b2005-04-28 01:31:13 +00001155 unsigned long realtime, cputime;
paul718e3742002-12-13 20:15:29 +00001156 RUSAGE_T ru;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001157
1158 /* Cache a pointer to the relevant cpu history thread, if the thread
1159 * does not have it yet.
1160 *
1161 * Callers submitting 'dummy threads' hence must take care that
1162 * thread->cpu is NULL
1163 */
1164 if (!thread->hist)
1165 {
1166 struct cpu_thread_history tmp;
1167
1168 tmp.func = thread->func;
1169 tmp.funcname = thread->funcname;
1170
1171 thread->hist = hash_get (cpu_record, &tmp,
1172 (void * (*) (void *))cpu_record_hash_alloc);
1173 }
paul718e3742002-12-13 20:15:29 +00001174
1175 GETRUSAGE (&thread->ru);
1176
1177 (*thread->func) (thread);
1178
1179 GETRUSAGE (&ru);
1180
ajs8b70d0b2005-04-28 01:31:13 +00001181 realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001182 thread->hist->real.total += realtime;
1183 if (thread->hist->real.max < realtime)
1184 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001185#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001186 thread->hist->cpu.total += cputime;
1187 if (thread->hist->cpu.max < cputime)
1188 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001189#endif
paule04ab742003-01-17 23:47:00 +00001190
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001191 ++(thread->hist->total_calls);
1192 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001193
ajs924b9222005-04-16 17:11:24 +00001194#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001195 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001196 {
1197 /*
1198 * We have a CPU Hog on our hands.
1199 * Whinge about it now, so we're aware this is yet another task
1200 * to fix.
1201 */
ajs8b70d0b2005-04-28 01:31:13 +00001202 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001203 thread->funcname,
1204 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001205 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001206 }
ajs924b9222005-04-16 17:11:24 +00001207#endif /* CONSUMED_TIME_CHECK */
Chris Caputo228da422009-07-18 05:44:03 +00001208
1209 XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname);
paul718e3742002-12-13 20:15:29 +00001210}
1211
1212/* Execute thread */
1213struct thread *
paule04ab742003-01-17 23:47:00 +00001214funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001215 int (*func)(struct thread *),
1216 void *arg,
paule04ab742003-01-17 23:47:00 +00001217 int val,
hasso8c328f12004-10-05 21:01:23 +00001218 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001219{
1220 struct thread dummy;
1221
1222 memset (&dummy, 0, sizeof (struct thread));
1223
1224 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001225 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001226 dummy.master = NULL;
1227 dummy.func = func;
1228 dummy.arg = arg;
1229 dummy.u.val = val;
paule04ab742003-01-17 23:47:00 +00001230 dummy.funcname = strip_funcname (funcname);
paul718e3742002-12-13 20:15:29 +00001231 thread_call (&dummy);
1232
paul9d11a192004-10-31 16:19:24 +00001233 XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
paul2946f652003-03-27 23:48:24 +00001234
paul718e3742002-12-13 20:15:29 +00001235 return NULL;
1236}