Annotation of embedaddon/quagga/lib/thread.c, revision 1.1
1.1 ! misho 1: /* 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"
! 29: #include "hash.h"
! 30: #include "command.h"
! 31: #include "sigevent.h"
! 32:
! 33: /* Recent absolute time of day */
! 34: struct timeval recent_time;
! 35: static struct timeval last_recent_time;
! 36: /* Relative time, since startup */
! 37: static struct timeval relative_time;
! 38: static struct timeval relative_time_base;
! 39: /* init flag */
! 40: static unsigned short timers_inited;
! 41:
! 42: static struct hash *cpu_record = NULL;
! 43:
! 44: /* Struct timeval's tv_usec one second value. */
! 45: #define TIMER_SECOND_MICRO 1000000L
! 46:
! 47: /* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
! 48: And change negative values to 0. */
! 49: static struct timeval
! 50: timeval_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)
! 65: /* Change negative timeouts to 0. */
! 66: a.tv_sec = a.tv_usec = 0;
! 67:
! 68: return a;
! 69: }
! 70:
! 71: static struct timeval
! 72: timeval_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:
! 82: static long
! 83: timeval_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:
! 89: static unsigned long
! 90: timeval_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:
! 96: #ifndef HAVE_CLOCK_MONOTONIC
! 97: static void
! 98: quagga_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 */
! 118: static int
! 119: quagga_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:
! 141: static int
! 142: quagga_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: */
! 169: static void
! 170: quagga_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: */
! 181: int
! 182: quagga_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: */
! 202: time_t
! 203: quagga_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 */
! 213: struct timeval
! 214: recent_relative_time (void)
! 215: {
! 216: return relative_time;
! 217: }
! 218:
! 219: static unsigned int
! 220: cpu_record_hash_key (struct cpu_thread_history *a)
! 221: {
! 222: return (uintptr_t) a->func;
! 223: }
! 224:
! 225: static int
! 226: cpu_record_hash_cmp (const struct cpu_thread_history *a,
! 227: const struct cpu_thread_history *b)
! 228: {
! 229: return a->func == b->func;
! 230: }
! 231:
! 232: static void *
! 233: cpu_record_hash_alloc (struct cpu_thread_history *a)
! 234: {
! 235: struct cpu_thread_history *new;
! 236: new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
! 237: new->func = a->func;
! 238: new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname);
! 239: return new;
! 240: }
! 241:
! 242: static void
! 243: cpu_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:
! 251: static inline void
! 252: vty_out_cpu_thread_history(struct vty* vty,
! 253: struct cpu_thread_history *a)
! 254: {
! 255: #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",
! 266: 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':' ',
! 271: a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
! 272: a->funcname, VTY_NEWLINE);
! 273: }
! 274:
! 275: static void
! 276: cpu_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];
! 281: thread_type *filter = args[2];
! 282: struct cpu_thread_history *a = bucket->data;
! 283:
! 284: a = bucket->data;
! 285: if ( !(a->types & *filter) )
! 286: return;
! 287: vty_out_cpu_thread_history(vty,a);
! 288: totals->total_calls += a->total_calls;
! 289: 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
! 297: }
! 298:
! 299: static void
! 300: cpu_record_print(struct vty *vty, thread_type filter)
! 301: {
! 302: struct cpu_thread_history tmp;
! 303: void *args[3] = {&tmp, vty, &filter};
! 304:
! 305: memset(&tmp, 0, sizeof tmp);
! 306: tmp.funcname = (char *)"TOTAL";
! 307: tmp.types = filter;
! 308:
! 309: #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);
! 318: 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:
! 326: DEFUN(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"
! 332: "Display filter (rwtexb)\n")
! 333: {
! 334: int i = 0;
! 335: thread_type filter = (thread_type) -1U;
! 336:
! 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;
! 364: case 'b':
! 365: case 'B':
! 366: filter |= (1 << THREAD_BACKGROUND);
! 367: break;
! 368: default:
! 369: break;
! 370: }
! 371: ++i;
! 372: }
! 373: if (filter == 0)
! 374: {
! 375: vty_out(vty, "Invalid filter \"%s\" specified,"
! 376: " must contain at least one of 'RWTEXB'%s",
! 377: argv[0], VTY_NEWLINE);
! 378: return CMD_WARNING;
! 379: }
! 380: }
! 381:
! 382: cpu_record_print(vty, filter);
! 383: return CMD_SUCCESS;
! 384: }
! 385:
! 386: static void
! 387: cpu_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:
! 400: static void
! 401: cpu_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:
! 409: DEFUN(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: }
! 468:
! 469: /* List allocation and head/tail print out. */
! 470: static void
! 471: thread_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. */
! 478: static void __attribute__ ((unused))
! 479: thread_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);
! 492: printf ("bgndlist : ");
! 493: thread_list_debug (&m->background);
! 494: printf ("total alloc: [%ld]\n", m->alloc);
! 495: printf ("-----------\n");
! 496: }
! 497:
! 498: /* Allocate new thread master. */
! 499: struct thread_master *
! 500: thread_master_create ()
! 501: {
! 502: if (cpu_record == NULL)
! 503: cpu_record
! 504: = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
! 505: (int (*) (const void *, const void *))cpu_record_hash_cmp);
! 506:
! 507: return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
! 508: sizeof (struct thread_master));
! 509: }
! 510:
! 511: /* Add a new thread to the list. */
! 512: static void
! 513: thread_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. */
! 526: static void
! 527: thread_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. */
! 542: static struct thread *
! 543: thread_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. */
! 559: static void
! 560: thread_add_unuse (struct thread_master *m, struct thread *thread)
! 561: {
! 562: assert (m != NULL && thread != NULL);
! 563: assert (thread->next == NULL);
! 564: assert (thread->prev == NULL);
! 565: assert (thread->type == THREAD_UNUSED);
! 566: thread_list_add (&m->unuse, thread);
! 567: /* XXX: Should we deallocate funcname here? */
! 568: }
! 569:
! 570: /* Free all unused thread. */
! 571: static void
! 572: thread_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;
! 580: if (t->funcname)
! 581: XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
! 582: XFREE (MTYPE_THREAD, t);
! 583: list->count--;
! 584: m->alloc--;
! 585: }
! 586: }
! 587:
! 588: /* Stop thread scheduler. */
! 589: void
! 590: thread_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);
! 598: thread_list_free (m, &m->background);
! 599:
! 600: XFREE (MTYPE_THREAD_MASTER, m);
! 601:
! 602: if (cpu_record)
! 603: {
! 604: hash_clean (cpu_record, cpu_record_hash_free);
! 605: hash_free (cpu_record);
! 606: cpu_record = NULL;
! 607: }
! 608: }
! 609:
! 610: /* Thread list is empty or not. */
! 611: static inline int
! 612: thread_empty (struct thread_list *list)
! 613: {
! 614: return list->head ? 0 : 1;
! 615: }
! 616:
! 617: /* Delete top of the list and return it. */
! 618: static struct thread *
! 619: thread_trim_head (struct thread_list *list)
! 620: {
! 621: if (!thread_empty (list))
! 622: return thread_list_delete (list, list->head);
! 623: return NULL;
! 624: }
! 625:
! 626: /* Return remain time in second. */
! 627: unsigned long
! 628: thread_timer_remain_second (struct thread *thread)
! 629: {
! 630: 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;
! 634: else
! 635: return 0;
! 636: }
! 637:
! 638: /* Trim blankspace and "()"s */
! 639: static char *
! 640: strip_funcname (const char *funcname)
! 641: {
! 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';
! 659: ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
! 660: *e = tmp;
! 661:
! 662: return ret;
! 663: }
! 664:
! 665: /* Get new thread. */
! 666: static struct thread *
! 667: thread_get (struct thread_master *m, u_char type,
! 668: int (*func) (struct thread *), void *arg, const char* funcname)
! 669: {
! 670: struct thread *thread;
! 671:
! 672: if (!thread_empty (&m->unuse))
! 673: {
! 674: thread = thread_trim_head (&m->unuse);
! 675: if (thread->funcname)
! 676: XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
! 677: }
! 678: else
! 679: {
! 680: thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
! 681: m->alloc++;
! 682: }
! 683: thread->type = type;
! 684: thread->add_type = type;
! 685: thread->master = m;
! 686: thread->func = func;
! 687: thread->arg = arg;
! 688:
! 689: thread->funcname = strip_funcname(funcname);
! 690:
! 691: return thread;
! 692: }
! 693:
! 694: /* Add new read thread. */
! 695: struct thread *
! 696: funcname_thread_add_read (struct thread_master *m,
! 697: int (*func) (struct thread *), void *arg, int fd, const char* funcname)
! 698: {
! 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:
! 709: thread = thread_get (m, THREAD_READ, func, arg, funcname);
! 710: 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. */
! 718: struct thread *
! 719: funcname_thread_add_write (struct thread_master *m,
! 720: int (*func) (struct thread *), void *arg, int fd, const char* funcname)
! 721: {
! 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:
! 732: thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
! 733: FD_SET (fd, &m->writefd);
! 734: thread->u.fd = fd;
! 735: thread_list_add (&m->write, thread);
! 736:
! 737: return thread;
! 738: }
! 739:
! 740: static struct thread *
! 741: funcname_thread_add_timer_timeval (struct thread_master *m,
! 742: int (*func) (struct thread *),
! 743: int type,
! 744: void *arg,
! 745: struct timeval *time_relative,
! 746: const char* funcname)
! 747: {
! 748: struct thread *thread;
! 749: struct thread_list *list;
! 750: struct timeval alarm_time;
! 751: struct thread *tt;
! 752:
! 753: assert (m != NULL);
! 754:
! 755: assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
! 756: assert (time_relative);
! 757:
! 758: list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
! 759: thread = thread_get (m, type, func, arg, funcname);
! 760:
! 761: /* Do we need jitter here? */
! 762: quagga_get_relative (NULL);
! 763: 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;
! 765: thread->u.sands = timeval_adjust(alarm_time);
! 766:
! 767: /* Sort by timeval. */
! 768: for (tt = list->head; tt; tt = tt->next)
! 769: if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
! 770: break;
! 771:
! 772: if (tt)
! 773: thread_list_add_before (list, tt, thread);
! 774: else
! 775: thread_list_add (list, thread);
! 776:
! 777: return thread;
! 778: }
! 779:
! 780:
! 781: /* Add timer event thread. */
! 782: struct thread *
! 783: funcname_thread_add_timer (struct thread_master *m,
! 784: int (*func) (struct thread *),
! 785: void *arg, long timer, const char* funcname)
! 786: {
! 787: struct timeval trel;
! 788:
! 789: assert (m != NULL);
! 790:
! 791: trel.tv_sec = timer;
! 792: trel.tv_usec = 0;
! 793:
! 794: return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
! 795: &trel, funcname);
! 796: }
! 797:
! 798: /* Add timer event thread with "millisecond" resolution */
! 799: struct thread *
! 800: funcname_thread_add_timer_msec (struct thread_master *m,
! 801: int (*func) (struct thread *),
! 802: void *arg, long timer, const char* funcname)
! 803: {
! 804: struct timeval trel;
! 805:
! 806: assert (m != NULL);
! 807:
! 808: trel.tv_sec = timer / 1000;
! 809: trel.tv_usec = 1000*(timer % 1000);
! 810:
! 811: 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 */
! 816: struct thread *
! 817: funcname_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);
! 839: }
! 840:
! 841: /* Add simple event thread. */
! 842: struct thread *
! 843: funcname_thread_add_event (struct thread_master *m,
! 844: int (*func) (struct thread *), void *arg, int val, const char* funcname)
! 845: {
! 846: struct thread *thread;
! 847:
! 848: assert (m != NULL);
! 849:
! 850: thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
! 851: thread->u.val = val;
! 852: thread_list_add (&m->event, thread);
! 853:
! 854: return thread;
! 855: }
! 856:
! 857: /* Cancel thread from scheduler. */
! 858: void
! 859: thread_cancel (struct thread *thread)
! 860: {
! 861: struct thread_list *list;
! 862:
! 863: 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);
! 868: list = &thread->master->read;
! 869: break;
! 870: case THREAD_WRITE:
! 871: assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
! 872: FD_CLR (thread->u.fd, &thread->master->writefd);
! 873: list = &thread->master->write;
! 874: break;
! 875: case THREAD_TIMER:
! 876: list = &thread->master->timer;
! 877: break;
! 878: case THREAD_EVENT:
! 879: list = &thread->master->event;
! 880: break;
! 881: case THREAD_READY:
! 882: list = &thread->master->ready;
! 883: break;
! 884: case THREAD_BACKGROUND:
! 885: list = &thread->master->background;
! 886: break;
! 887: default:
! 888: return;
! 889: break;
! 890: }
! 891: thread_list_delete (list, thread);
! 892: thread->type = THREAD_UNUSED;
! 893: thread_add_unuse (thread->master, thread);
! 894: }
! 895:
! 896: /* Delete all events which has argument value arg. */
! 897: unsigned int
! 898: thread_cancel_event (struct thread_master *m, void *arg)
! 899: {
! 900: unsigned int ret = 0;
! 901: 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)
! 912: {
! 913: ret++;
! 914: thread_list_delete (&m->event, t);
! 915: t->type = THREAD_UNUSED;
! 916: thread_add_unuse (m, t);
! 917: }
! 918: }
! 919: return ret;
! 920: }
! 921:
! 922: static struct timeval *
! 923: thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
! 924: {
! 925: if (!thread_empty (tlist))
! 926: {
! 927: *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
! 928: return timer_val;
! 929: }
! 930: return NULL;
! 931: }
! 932:
! 933: static struct thread *
! 934: thread_run (struct thread_master *m, struct thread *thread,
! 935: struct thread *fetch)
! 936: {
! 937: *fetch = *thread;
! 938: thread->type = THREAD_UNUSED;
! 939: thread->funcname = NULL; /* thread_call will free fetch's copied pointer */
! 940: thread_add_unuse (m, thread);
! 941: return fetch;
! 942: }
! 943:
! 944: static int
! 945: thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
! 946: {
! 947: struct thread *thread;
! 948: struct thread *next;
! 949: int ready = 0;
! 950:
! 951: assert (list);
! 952:
! 953: for (thread = list->head; thread; thread = next)
! 954: {
! 955: next = thread->next;
! 956:
! 957: if (FD_ISSET (THREAD_FD (thread), fdset))
! 958: {
! 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: }
! 966: }
! 967: return ready;
! 968: }
! 969:
! 970: /* Add all timers that have popped to the ready list. */
! 971: static unsigned int
! 972: thread_timer_process (struct thread_list *list, struct timeval *timenow)
! 973: {
! 974: struct thread *thread;
! 975: unsigned int ready = 0;
! 976:
! 977: for (thread = list->head; thread; thread = thread->next)
! 978: {
! 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: }
! 986: return ready;
! 987: }
! 988:
! 989: /* process a list en masse, e.g. for event thread lists */
! 990: static unsigned int
! 991: thread_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:
! 1007: /* Fetch next ready thread. */
! 1008: struct thread *
! 1009: thread_fetch (struct thread_master *m, struct thread *fetch)
! 1010: {
! 1011: struct thread *thread;
! 1012: fd_set readfd;
! 1013: fd_set writefd;
! 1014: fd_set exceptfd;
! 1015: struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
! 1016: struct timeval timer_val_bg;
! 1017: struct timeval *timer_wait = &timer_val;
! 1018: struct timeval *timer_wait_bg;
! 1019:
! 1020: while (1)
! 1021: {
! 1022: int num = 0;
! 1023:
! 1024: /* Signals pre-empt everything */
! 1025: quagga_sigevent_process ();
! 1026:
! 1027: /* Drain the ready queue of already scheduled jobs, before scheduling
! 1028: * more.
! 1029: */
! 1030: if ((thread = thread_trim_head (&m->ready)) != NULL)
! 1031: return thread_run (m, thread, fetch);
! 1032:
! 1033: /* 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:
! 1041: /* Structure copy. */
! 1042: readfd = m->readfd;
! 1043: writefd = m->writefd;
! 1044: exceptfd = m->exceptfd;
! 1045:
! 1046: /* Calculate select wait timer if nothing else to do */
! 1047: 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: }
! 1057:
! 1058: num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
! 1059:
! 1060: /* Signals should get quick treatment */
! 1061: if (num < 0)
! 1062: {
! 1063: if (errno == EINTR)
! 1064: continue; /* signal received - process it */
! 1065: zlog_warn ("select() error: %s", safe_strerror (errno));
! 1066: return NULL;
! 1067: }
! 1068:
! 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. */
! 1072: quagga_get_relative (NULL);
! 1073: thread_timer_process (&m->timer, &relative_time);
! 1074:
! 1075: /* Got IO, process it */
! 1076: if (num > 0)
! 1077: {
! 1078: /* Normal priority read thead. */
! 1079: thread_process_fd (&m->read, &readfd, &m->readfd);
! 1080: /* Write thead. */
! 1081: thread_process_fd (&m->write, &writefd, &m->writefd);
! 1082: }
! 1083:
! 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:
! 1093: /* Background timer/events, lowest priority */
! 1094: thread_timer_process (&m->background, &relative_time);
! 1095:
! 1096: if ((thread = thread_trim_head (&m->ready)) != NULL)
! 1097: return thread_run (m, thread, fetch);
! 1098: }
! 1099: }
! 1100:
! 1101: unsigned long
! 1102: thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
! 1103: {
! 1104: #ifdef HAVE_RUSAGE
! 1105: /* This is 'user + sys' time. */
! 1106: *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
! 1107: timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
! 1108: #else
! 1109: *cputime = 0;
! 1110: #endif /* HAVE_RUSAGE */
! 1111: return timeval_elapsed (now->real, start->real);
! 1112: }
! 1113:
! 1114: /* 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. */
! 1123: int
! 1124: thread_should_yield (struct thread *thread)
! 1125: {
! 1126: quagga_get_relative (NULL);
! 1127: return (timeval_elapsed(relative_time, thread->ru.real) >
! 1128: THREAD_YIELD_TIME_SLOT);
! 1129: }
! 1130:
! 1131: void
! 1132: thread_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:
! 1149: /* We check thread consumed time. If the system has getrusage, we'll
! 1150: use that to get in-depth stats on the performance of the thread in addition
! 1151: to wall clock time stats from gettimeofday. */
! 1152: void
! 1153: thread_call (struct thread *thread)
! 1154: {
! 1155: unsigned long realtime, cputime;
! 1156: RUSAGE_T ru;
! 1157:
! 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: }
! 1174:
! 1175: GETRUSAGE (&thread->ru);
! 1176:
! 1177: (*thread->func) (thread);
! 1178:
! 1179: GETRUSAGE (&ru);
! 1180:
! 1181: realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
! 1182: thread->hist->real.total += realtime;
! 1183: if (thread->hist->real.max < realtime)
! 1184: thread->hist->real.max = realtime;
! 1185: #ifdef HAVE_RUSAGE
! 1186: thread->hist->cpu.total += cputime;
! 1187: if (thread->hist->cpu.max < cputime)
! 1188: thread->hist->cpu.max = cputime;
! 1189: #endif
! 1190:
! 1191: ++(thread->hist->total_calls);
! 1192: thread->hist->types |= (1 << thread->add_type);
! 1193:
! 1194: #ifdef CONSUMED_TIME_CHECK
! 1195: if (realtime > CONSUMED_TIME_CHECK)
! 1196: {
! 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: */
! 1202: zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
! 1203: thread->funcname,
! 1204: (unsigned long) thread->func,
! 1205: realtime/1000, cputime/1000);
! 1206: }
! 1207: #endif /* CONSUMED_TIME_CHECK */
! 1208:
! 1209: XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname);
! 1210: }
! 1211:
! 1212: /* Execute thread */
! 1213: struct thread *
! 1214: funcname_thread_execute (struct thread_master *m,
! 1215: int (*func)(struct thread *),
! 1216: void *arg,
! 1217: int val,
! 1218: const char* funcname)
! 1219: {
! 1220: struct thread dummy;
! 1221:
! 1222: memset (&dummy, 0, sizeof (struct thread));
! 1223:
! 1224: dummy.type = THREAD_EVENT;
! 1225: dummy.add_type = THREAD_EXECUTE;
! 1226: dummy.master = NULL;
! 1227: dummy.func = func;
! 1228: dummy.arg = arg;
! 1229: dummy.u.val = val;
! 1230: dummy.funcname = strip_funcname (funcname);
! 1231: thread_call (&dummy);
! 1232:
! 1233: XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
! 1234:
! 1235: return NULL;
! 1236: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>