blob: e89af541c61f7ddaaa1709d4bb5e444f775785d0 [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);
306 tmp.funcname = "TOTAL";
307 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}
385
paul718e3742002-12-13 20:15:29 +0000386/* List allocation and head/tail print out. */
387static void
388thread_list_debug (struct thread_list *list)
389{
390 printf ("count [%d] head [%p] tail [%p]\n",
391 list->count, list->head, list->tail);
392}
393
394/* Debug print for thread_master. */
paul8cc41982005-05-06 21:25:49 +0000395static void __attribute__ ((unused))
paul718e3742002-12-13 20:15:29 +0000396thread_master_debug (struct thread_master *m)
397{
398 printf ("-----------\n");
399 printf ("readlist : ");
400 thread_list_debug (&m->read);
401 printf ("writelist : ");
402 thread_list_debug (&m->write);
403 printf ("timerlist : ");
404 thread_list_debug (&m->timer);
405 printf ("eventlist : ");
406 thread_list_debug (&m->event);
407 printf ("unuselist : ");
408 thread_list_debug (&m->unuse);
paula48b4e62005-04-22 00:43:47 +0000409 printf ("bgndlist : ");
410 thread_list_debug (&m->background);
paul718e3742002-12-13 20:15:29 +0000411 printf ("total alloc: [%ld]\n", m->alloc);
412 printf ("-----------\n");
413}
414
415/* Allocate new thread master. */
416struct thread_master *
417thread_master_create ()
418{
paule04ab742003-01-17 23:47:00 +0000419 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000420 cpu_record
421 = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100422 (int (*) (const void *, const void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000423
paul718e3742002-12-13 20:15:29 +0000424 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
425 sizeof (struct thread_master));
426}
427
428/* Add a new thread to the list. */
429static void
430thread_list_add (struct thread_list *list, struct thread *thread)
431{
432 thread->next = NULL;
433 thread->prev = list->tail;
434 if (list->tail)
435 list->tail->next = thread;
436 else
437 list->head = thread;
438 list->tail = thread;
439 list->count++;
440}
441
442/* Add a new thread just before the point. */
443static void
444thread_list_add_before (struct thread_list *list,
445 struct thread *point,
446 struct thread *thread)
447{
448 thread->next = point;
449 thread->prev = point->prev;
450 if (point->prev)
451 point->prev->next = thread;
452 else
453 list->head = thread;
454 point->prev = thread;
455 list->count++;
456}
457
458/* Delete a thread from the list. */
459static struct thread *
460thread_list_delete (struct thread_list *list, struct thread *thread)
461{
462 if (thread->next)
463 thread->next->prev = thread->prev;
464 else
465 list->tail = thread->prev;
466 if (thread->prev)
467 thread->prev->next = thread->next;
468 else
469 list->head = thread->next;
470 thread->next = thread->prev = NULL;
471 list->count--;
472 return thread;
473}
474
475/* Move thread to unuse list. */
476static void
477thread_add_unuse (struct thread_master *m, struct thread *thread)
478{
paula48b4e62005-04-22 00:43:47 +0000479 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000480 assert (thread->next == NULL);
481 assert (thread->prev == NULL);
482 assert (thread->type == THREAD_UNUSED);
483 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000484 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000485}
486
487/* Free all unused thread. */
488static void
489thread_list_free (struct thread_master *m, struct thread_list *list)
490{
491 struct thread *t;
492 struct thread *next;
493
494 for (t = list->head; t; t = next)
495 {
496 next = t->next;
Chris Caputo228da422009-07-18 05:44:03 +0000497 if (t->funcname)
498 XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
paul718e3742002-12-13 20:15:29 +0000499 XFREE (MTYPE_THREAD, t);
500 list->count--;
501 m->alloc--;
502 }
503}
504
505/* Stop thread scheduler. */
506void
507thread_master_free (struct thread_master *m)
508{
509 thread_list_free (m, &m->read);
510 thread_list_free (m, &m->write);
511 thread_list_free (m, &m->timer);
512 thread_list_free (m, &m->event);
513 thread_list_free (m, &m->ready);
514 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000515 thread_list_free (m, &m->background);
516
paul718e3742002-12-13 20:15:29 +0000517 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000518
519 if (cpu_record)
520 {
521 hash_clean (cpu_record, cpu_record_hash_free);
522 hash_free (cpu_record);
523 cpu_record = NULL;
524 }
paul718e3742002-12-13 20:15:29 +0000525}
526
paul8cc41982005-05-06 21:25:49 +0000527/* Thread list is empty or not. */
528static inline int
529thread_empty (struct thread_list *list)
530{
531 return list->head ? 0 : 1;
532}
533
paul718e3742002-12-13 20:15:29 +0000534/* Delete top of the list and return it. */
535static struct thread *
536thread_trim_head (struct thread_list *list)
537{
paul8cc41982005-05-06 21:25:49 +0000538 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000539 return thread_list_delete (list, list->head);
540 return NULL;
541}
542
paul718e3742002-12-13 20:15:29 +0000543/* Return remain time in second. */
544unsigned long
545thread_timer_remain_second (struct thread *thread)
546{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000547 quagga_get_relative (NULL);
548
549 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
550 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000551 else
552 return 0;
553}
554
paule04ab742003-01-17 23:47:00 +0000555/* Trim blankspace and "()"s */
556static char *
hasso8c328f12004-10-05 21:01:23 +0000557strip_funcname (const char *funcname)
paule04ab742003-01-17 23:47:00 +0000558{
559 char buff[100];
560 char tmp, *ret, *e, *b = buff;
561
562 strncpy(buff, funcname, sizeof(buff));
563 buff[ sizeof(buff) -1] = '\0';
564 e = buff +strlen(buff) -1;
565
566 /* Wont work for funcname == "Word (explanation)" */
567
568 while (*b == ' ' || *b == '(')
569 ++b;
570 while (*e == ' ' || *e == ')')
571 --e;
572 e++;
573
574 tmp = *e;
575 *e = '\0';
paul9d11a192004-10-31 16:19:24 +0000576 ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
paule04ab742003-01-17 23:47:00 +0000577 *e = tmp;
578
579 return ret;
580}
581
paul718e3742002-12-13 20:15:29 +0000582/* Get new thread. */
583static struct thread *
584thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000585 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000586{
587 struct thread *thread;
588
paul8cc41982005-05-06 21:25:49 +0000589 if (!thread_empty (&m->unuse))
paul2946f652003-03-27 23:48:24 +0000590 {
591 thread = thread_trim_head (&m->unuse);
592 if (thread->funcname)
paul9d11a192004-10-31 16:19:24 +0000593 XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
paul2946f652003-03-27 23:48:24 +0000594 }
paul718e3742002-12-13 20:15:29 +0000595 else
596 {
597 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
598 m->alloc++;
599 }
600 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000601 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000602 thread->master = m;
603 thread->func = func;
604 thread->arg = arg;
605
paule04ab742003-01-17 23:47:00 +0000606 thread->funcname = strip_funcname(funcname);
607
paul718e3742002-12-13 20:15:29 +0000608 return thread;
609}
610
611/* Add new read thread. */
612struct thread *
paule04ab742003-01-17 23:47:00 +0000613funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000614 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000615{
616 struct thread *thread;
617
618 assert (m != NULL);
619
620 if (FD_ISSET (fd, &m->readfd))
621 {
622 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
623 return NULL;
624 }
625
paule04ab742003-01-17 23:47:00 +0000626 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000627 FD_SET (fd, &m->readfd);
628 thread->u.fd = fd;
629 thread_list_add (&m->read, thread);
630
631 return thread;
632}
633
634/* Add new write thread. */
635struct thread *
paule04ab742003-01-17 23:47:00 +0000636funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000637 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000638{
639 struct thread *thread;
640
641 assert (m != NULL);
642
643 if (FD_ISSET (fd, &m->writefd))
644 {
645 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
646 return NULL;
647 }
648
paule04ab742003-01-17 23:47:00 +0000649 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000650 FD_SET (fd, &m->writefd);
651 thread->u.fd = fd;
652 thread_list_add (&m->write, thread);
653
654 return thread;
655}
656
paul98c91ac2004-10-05 14:57:50 +0000657static struct thread *
658funcname_thread_add_timer_timeval (struct thread_master *m,
659 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000660 int type,
paul98c91ac2004-10-05 14:57:50 +0000661 void *arg,
662 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000663 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000664{
paul718e3742002-12-13 20:15:29 +0000665 struct thread *thread;
paula48b4e62005-04-22 00:43:47 +0000666 struct thread_list *list;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000667 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000668 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000669
670 assert (m != NULL);
671
ajs8b70d0b2005-04-28 01:31:13 +0000672 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000673 assert (time_relative);
674
ajs8b70d0b2005-04-28 01:31:13 +0000675 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000676 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000677
678 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100679 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000680 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
681 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000682 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000683
684 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000685 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000686 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
687 break;
688
689 if (tt)
paula48b4e62005-04-22 00:43:47 +0000690 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000691 else
paula48b4e62005-04-22 00:43:47 +0000692 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000693
694 return thread;
695}
696
paul98c91ac2004-10-05 14:57:50 +0000697
698/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000699struct thread *
paul98c91ac2004-10-05 14:57:50 +0000700funcname_thread_add_timer (struct thread_master *m,
701 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000702 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000703{
paul98c91ac2004-10-05 14:57:50 +0000704 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000705
706 assert (m != NULL);
707
paul9076fbd2004-10-11 09:40:58 +0000708 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000709 trel.tv_usec = 0;
710
paula48b4e62005-04-22 00:43:47 +0000711 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
712 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000713}
714
715/* Add timer event thread with "millisecond" resolution */
716struct thread *
717funcname_thread_add_timer_msec (struct thread_master *m,
718 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000719 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000720{
721 struct timeval trel;
722
723 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000724
ajsaf04bd72004-12-28 17:00:12 +0000725 trel.tv_sec = timer / 1000;
726 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000727
paula48b4e62005-04-22 00:43:47 +0000728 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
729 arg, &trel, funcname);
730}
731
732/* Add a background thread, with an optional millisec delay */
733struct thread *
734funcname_thread_add_background (struct thread_master *m,
735 int (*func) (struct thread *),
736 void *arg, long delay,
737 const char *funcname)
738{
739 struct timeval trel;
740
741 assert (m != NULL);
742
743 if (delay)
744 {
745 trel.tv_sec = delay / 1000;
746 trel.tv_usec = 1000*(delay % 1000);
747 }
748 else
749 {
750 trel.tv_sec = 0;
751 trel.tv_usec = 0;
752 }
753
754 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
755 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000756}
757
paul718e3742002-12-13 20:15:29 +0000758/* Add simple event thread. */
759struct thread *
paule04ab742003-01-17 23:47:00 +0000760funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000761 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000762{
763 struct thread *thread;
764
765 assert (m != NULL);
766
paule04ab742003-01-17 23:47:00 +0000767 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000768 thread->u.val = val;
769 thread_list_add (&m->event, thread);
770
771 return thread;
772}
773
774/* Cancel thread from scheduler. */
775void
776thread_cancel (struct thread *thread)
777{
paula48b4e62005-04-22 00:43:47 +0000778 struct thread_list *list;
779
paul718e3742002-12-13 20:15:29 +0000780 switch (thread->type)
781 {
782 case THREAD_READ:
783 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
784 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000785 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000786 break;
787 case THREAD_WRITE:
788 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
789 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000790 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000791 break;
792 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000793 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000794 break;
795 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000796 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000797 break;
798 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000799 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000800 break;
paula48b4e62005-04-22 00:43:47 +0000801 case THREAD_BACKGROUND:
802 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000803 break;
paul718e3742002-12-13 20:15:29 +0000804 default:
paula48b4e62005-04-22 00:43:47 +0000805 return;
paul718e3742002-12-13 20:15:29 +0000806 break;
807 }
paula48b4e62005-04-22 00:43:47 +0000808 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000809 thread->type = THREAD_UNUSED;
810 thread_add_unuse (thread->master, thread);
811}
812
813/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000814unsigned int
paul718e3742002-12-13 20:15:29 +0000815thread_cancel_event (struct thread_master *m, void *arg)
816{
pauldc818072005-05-19 01:30:53 +0000817 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000818 struct thread *thread;
819
820 thread = m->event.head;
821 while (thread)
822 {
823 struct thread *t;
824
825 t = thread;
826 thread = t->next;
827
828 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000829 {
pauldc818072005-05-19 01:30:53 +0000830 ret++;
paula48b4e62005-04-22 00:43:47 +0000831 thread_list_delete (&m->event, t);
832 t->type = THREAD_UNUSED;
833 thread_add_unuse (m, t);
834 }
paul718e3742002-12-13 20:15:29 +0000835 }
pauldc818072005-05-19 01:30:53 +0000836 return ret;
paul718e3742002-12-13 20:15:29 +0000837}
838
paula48b4e62005-04-22 00:43:47 +0000839static struct timeval *
840thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000841{
paul8cc41982005-05-06 21:25:49 +0000842 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000843 {
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000844 *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000845 return timer_val;
846 }
847 return NULL;
848}
paul718e3742002-12-13 20:15:29 +0000849
paul8cc41982005-05-06 21:25:49 +0000850static struct thread *
paul718e3742002-12-13 20:15:29 +0000851thread_run (struct thread_master *m, struct thread *thread,
852 struct thread *fetch)
853{
854 *fetch = *thread;
855 thread->type = THREAD_UNUSED;
Chris Caputo228da422009-07-18 05:44:03 +0000856 thread->funcname = NULL; /* thread_call will free fetch's copied pointer */
paul718e3742002-12-13 20:15:29 +0000857 thread_add_unuse (m, thread);
858 return fetch;
859}
860
paula48b4e62005-04-22 00:43:47 +0000861static int
862thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000863{
864 struct thread *thread;
865 struct thread *next;
866 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000867
868 assert (list);
869
paul718e3742002-12-13 20:15:29 +0000870 for (thread = list->head; thread; thread = next)
871 {
872 next = thread->next;
873
874 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000875 {
876 assert (FD_ISSET (THREAD_FD (thread), mfdset));
877 FD_CLR(THREAD_FD (thread), mfdset);
878 thread_list_delete (list, thread);
879 thread_list_add (&thread->master->ready, thread);
880 thread->type = THREAD_READY;
881 ready++;
882 }
paul718e3742002-12-13 20:15:29 +0000883 }
884 return ready;
885}
886
ajs8b70d0b2005-04-28 01:31:13 +0000887/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000888static unsigned int
889thread_timer_process (struct thread_list *list, struct timeval *timenow)
890{
891 struct thread *thread;
892 unsigned int ready = 0;
893
paula48b4e62005-04-22 00:43:47 +0000894 for (thread = list->head; thread; thread = thread->next)
ajs8b70d0b2005-04-28 01:31:13 +0000895 {
896 if (timeval_cmp (*timenow, thread->u.sands) < 0)
897 return ready;
898 thread_list_delete (list, thread);
899 thread->type = THREAD_READY;
900 thread_list_add (&thread->master->ready, thread);
901 ready++;
902 }
paula48b4e62005-04-22 00:43:47 +0000903 return ready;
904}
905
paul718e3742002-12-13 20:15:29 +0000906/* Fetch next ready thread. */
907struct thread *
908thread_fetch (struct thread_master *m, struct thread *fetch)
909{
paul718e3742002-12-13 20:15:29 +0000910 struct thread *thread;
911 fd_set readfd;
912 fd_set writefd;
913 fd_set exceptfd;
paul718e3742002-12-13 20:15:29 +0000914 struct timeval timer_val;
paula48b4e62005-04-22 00:43:47 +0000915 struct timeval timer_val_bg;
paul718e3742002-12-13 20:15:29 +0000916 struct timeval *timer_wait;
paula48b4e62005-04-22 00:43:47 +0000917 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +0000918
919 while (1)
920 {
paula48b4e62005-04-22 00:43:47 +0000921 int num = 0;
paula48b4e62005-04-22 00:43:47 +0000922
paul05c447d2004-07-22 19:14:27 +0000923 /* Signals are highest priority */
924 quagga_sigevent_process ();
925
926 /* Normal event are the next highest priority. */
paul718e3742002-12-13 20:15:29 +0000927 if ((thread = thread_trim_head (&m->event)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000928 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000929
paula48b4e62005-04-22 00:43:47 +0000930 /* If there are any ready threads from previous scheduler runs,
931 * process top of them.
932 */
paul718e3742002-12-13 20:15:29 +0000933 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000934 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +0000935
paul718e3742002-12-13 20:15:29 +0000936 /* Structure copy. */
937 readfd = m->readfd;
938 writefd = m->writefd;
939 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +0000940
941 /* Calculate select wait timer if nothing else to do */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000942 quagga_get_relative (NULL);
ajs8b70d0b2005-04-28 01:31:13 +0000943 timer_wait = thread_timer_wait (&m->timer, &timer_val);
944 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
945
946 if (timer_wait_bg &&
947 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
948 timer_wait = timer_wait_bg;
paula48b4e62005-04-22 00:43:47 +0000949
paul718e3742002-12-13 20:15:29 +0000950 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +0000951
952 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +0000953 if (num < 0)
paul05c447d2004-07-22 19:14:27 +0000954 {
955 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +0000956 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +0000957 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +0000958 return NULL;
959 }
ajs8b70d0b2005-04-28 01:31:13 +0000960
961 /* Check foreground timers. Historically, they have had higher
962 priority than I/O threads, so let's push them onto the ready
963 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000964 quagga_get_relative (NULL);
965 thread_timer_process (&m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +0000966
967 /* Got IO, process it */
968 if (num > 0)
969 {
970 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000971 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +0000972 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +0000973 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +0000974 }
ajs8b70d0b2005-04-28 01:31:13 +0000975
976#if 0
977 /* If any threads were made ready above (I/O or foreground timer),
978 perhaps we should avoid adding background timers to the ready
979 list at this time. If this is code is uncommented, then background
980 timer threads will not run unless there is nothing else to do. */
981 if ((thread = thread_trim_head (&m->ready)) != NULL)
982 return thread_run (m, thread, fetch);
983#endif
984
paula48b4e62005-04-22 00:43:47 +0000985 /* Background timer/events, lowest priority */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000986 thread_timer_process (&m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +0000987
ajs8b70d0b2005-04-28 01:31:13 +0000988 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +0000989 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +0000990 }
991}
992
ajs924b9222005-04-16 17:11:24 +0000993unsigned long
ajs8b70d0b2005-04-28 01:31:13 +0000994thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +0000995{
paul718e3742002-12-13 20:15:29 +0000996#ifdef HAVE_RUSAGE
997 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +0000998 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
999 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001000#else
ajs8b70d0b2005-04-28 01:31:13 +00001001 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001002#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001003 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001004}
1005
ajs8b70d0b2005-04-28 01:31:13 +00001006/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1007 Note: we are using real (wall clock) time for this calculation.
1008 It could be argued that CPU time may make more sense in certain
1009 contexts. The things to consider are whether the thread may have
1010 blocked (in which case wall time increases, but CPU time does not),
1011 or whether the system is heavily loaded with other processes competing
1012 for CPU time. On balance, wall clock time seems to make sense.
1013 Plus it has the added benefit that gettimeofday should be faster
1014 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001015int
1016thread_should_yield (struct thread *thread)
1017{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001018 quagga_get_relative (NULL);
1019 return (timeval_elapsed(relative_time, thread->ru.real) >
ajs8b70d0b2005-04-28 01:31:13 +00001020 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001021}
1022
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001023void
1024thread_getrusage (RUSAGE_T *r)
1025{
1026 quagga_get_relative (NULL);
1027#ifdef HAVE_RUSAGE
1028 getrusage(RUSAGE_SELF, &(r->cpu));
1029#endif
1030 r->real = relative_time;
1031
1032#ifdef HAVE_CLOCK_MONOTONIC
1033 /* quagga_get_relative() only updates recent_time if gettimeofday
1034 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1035 * and guarantee to update it before threads are run...
1036 */
1037 quagga_gettimeofday(&recent_time);
1038#endif /* HAVE_CLOCK_MONOTONIC */
1039}
1040
paul718e3742002-12-13 20:15:29 +00001041/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001042 use that to get in-depth stats on the performance of the thread in addition
1043 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001044void
1045thread_call (struct thread *thread)
1046{
ajs8b70d0b2005-04-28 01:31:13 +00001047 unsigned long realtime, cputime;
paul718e3742002-12-13 20:15:29 +00001048 RUSAGE_T ru;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001049
1050 /* Cache a pointer to the relevant cpu history thread, if the thread
1051 * does not have it yet.
1052 *
1053 * Callers submitting 'dummy threads' hence must take care that
1054 * thread->cpu is NULL
1055 */
1056 if (!thread->hist)
1057 {
1058 struct cpu_thread_history tmp;
1059
1060 tmp.func = thread->func;
1061 tmp.funcname = thread->funcname;
1062
1063 thread->hist = hash_get (cpu_record, &tmp,
1064 (void * (*) (void *))cpu_record_hash_alloc);
1065 }
paul718e3742002-12-13 20:15:29 +00001066
1067 GETRUSAGE (&thread->ru);
1068
1069 (*thread->func) (thread);
1070
1071 GETRUSAGE (&ru);
1072
ajs8b70d0b2005-04-28 01:31:13 +00001073 realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001074 thread->hist->real.total += realtime;
1075 if (thread->hist->real.max < realtime)
1076 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001077#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001078 thread->hist->cpu.total += cputime;
1079 if (thread->hist->cpu.max < cputime)
1080 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001081#endif
paule04ab742003-01-17 23:47:00 +00001082
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001083 ++(thread->hist->total_calls);
1084 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001085
ajs924b9222005-04-16 17:11:24 +00001086#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001087 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001088 {
1089 /*
1090 * We have a CPU Hog on our hands.
1091 * Whinge about it now, so we're aware this is yet another task
1092 * to fix.
1093 */
ajs8b70d0b2005-04-28 01:31:13 +00001094 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001095 thread->funcname,
1096 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001097 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001098 }
ajs924b9222005-04-16 17:11:24 +00001099#endif /* CONSUMED_TIME_CHECK */
Chris Caputo228da422009-07-18 05:44:03 +00001100
1101 XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname);
paul718e3742002-12-13 20:15:29 +00001102}
1103
1104/* Execute thread */
1105struct thread *
paule04ab742003-01-17 23:47:00 +00001106funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001107 int (*func)(struct thread *),
1108 void *arg,
paule04ab742003-01-17 23:47:00 +00001109 int val,
hasso8c328f12004-10-05 21:01:23 +00001110 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001111{
1112 struct thread dummy;
1113
1114 memset (&dummy, 0, sizeof (struct thread));
1115
1116 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001117 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001118 dummy.master = NULL;
1119 dummy.func = func;
1120 dummy.arg = arg;
1121 dummy.u.val = val;
paule04ab742003-01-17 23:47:00 +00001122 dummy.funcname = strip_funcname (funcname);
paul718e3742002-12-13 20:15:29 +00001123 thread_call (&dummy);
1124
paul9d11a192004-10-31 16:19:24 +00001125 XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
paul2946f652003-03-27 23:48:24 +00001126
paul718e3742002-12-13 20:15:29 +00001127 return NULL;
1128}