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>