]>
git.proxmox.com Git - mirror_frr.git/blob - lib/thread.c
1 /* Thread management routine
2 * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
4 * This file is part of GNU Zebra.
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
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.
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
25 #include <sys/resource.h>
35 DEFINE_MTYPE_STATIC(LIB
, THREAD
, "Thread")
36 DEFINE_MTYPE_STATIC(LIB
, THREAD_MASTER
, "Thread master")
37 DEFINE_MTYPE_STATIC(LIB
, THREAD_STATS
, "Thread stats")
39 #if defined(__APPLE__)
40 #include <mach/mach.h>
41 #include <mach/mach_time.h>
44 /* Relative time, since startup */
45 static struct hash
*cpu_record
= NULL
;
47 static unsigned long timeval_elapsed(struct timeval a
, struct timeval b
)
49 return (((a
.tv_sec
- b
.tv_sec
) * TIMER_SECOND_MICRO
)
50 + (a
.tv_usec
- b
.tv_usec
));
53 static unsigned int cpu_record_hash_key(struct cpu_thread_history
*a
)
55 return (uintptr_t)a
->func
;
58 static int cpu_record_hash_cmp(const struct cpu_thread_history
*a
,
59 const struct cpu_thread_history
*b
)
61 return a
->func
== b
->func
;
64 static void *cpu_record_hash_alloc(struct cpu_thread_history
*a
)
66 struct cpu_thread_history
*new;
67 new = XCALLOC(MTYPE_THREAD_STATS
, sizeof(struct cpu_thread_history
));
69 new->funcname
= a
->funcname
;
73 static void cpu_record_hash_free(void *a
)
75 struct cpu_thread_history
*hist
= a
;
77 XFREE(MTYPE_THREAD_STATS
, hist
);
80 static void vty_out_cpu_thread_history(struct vty
*vty
,
81 struct cpu_thread_history
*a
)
83 vty_out(vty
, "%5d %10ld.%03ld %9d %8ld %9ld %8ld %9ld", a
->total_active
,
84 a
->cpu
.total
/ 1000, a
->cpu
.total
% 1000, a
->total_calls
,
85 a
->cpu
.total
/ a
->total_calls
, a
->cpu
.max
,
86 a
->real
.total
/ a
->total_calls
, a
->real
.max
);
87 vty_out(vty
, " %c%c%c%c%c%c %s%s",
88 a
->types
& (1 << THREAD_READ
) ? 'R' : ' ',
89 a
->types
& (1 << THREAD_WRITE
) ? 'W' : ' ',
90 a
->types
& (1 << THREAD_TIMER
) ? 'T' : ' ',
91 a
->types
& (1 << THREAD_EVENT
) ? 'E' : ' ',
92 a
->types
& (1 << THREAD_EXECUTE
) ? 'X' : ' ',
93 a
->types
& (1 << THREAD_BACKGROUND
) ? 'B' : ' ', a
->funcname
,
97 static void cpu_record_hash_print(struct hash_backet
*bucket
, void *args
[])
99 struct cpu_thread_history
*totals
= args
[0];
100 struct vty
*vty
= args
[1];
101 thread_type
*filter
= args
[2];
102 struct cpu_thread_history
*a
= bucket
->data
;
104 if (!(a
->types
& *filter
))
106 vty_out_cpu_thread_history(vty
, a
);
107 totals
->total_active
+= a
->total_active
;
108 totals
->total_calls
+= a
->total_calls
;
109 totals
->real
.total
+= a
->real
.total
;
110 if (totals
->real
.max
< a
->real
.max
)
111 totals
->real
.max
= a
->real
.max
;
112 totals
->cpu
.total
+= a
->cpu
.total
;
113 if (totals
->cpu
.max
< a
->cpu
.max
)
114 totals
->cpu
.max
= a
->cpu
.max
;
117 static void cpu_record_print(struct vty
*vty
, thread_type filter
)
119 struct cpu_thread_history tmp
;
120 void *args
[3] = {&tmp
, vty
, &filter
};
122 memset(&tmp
, 0, sizeof tmp
);
123 tmp
.funcname
= "TOTAL";
126 vty_out(vty
, "%21s %18s %18s%s", "",
127 "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE
);
128 vty_out(vty
, "Active Runtime(ms) Invoked Avg uSec Max uSecs");
129 vty_out(vty
, " Avg uSec Max uSecs");
130 vty_out(vty
, " Type Thread%s", VTY_NEWLINE
);
131 hash_iterate(cpu_record
, (void (*)(struct hash_backet
*,
132 void *))cpu_record_hash_print
,
135 if (tmp
.total_calls
> 0)
136 vty_out_cpu_thread_history(vty
, &tmp
);
139 DEFUN (show_thread_cpu
,
141 "show thread cpu [FILTER]",
143 "Thread information\n"
145 "Display filter (rwtexb)\n")
149 thread_type filter
= (thread_type
)-1U;
153 while (argv
[idx_filter
]->arg
[i
] != '\0') {
154 switch (argv
[idx_filter
]->arg
[i
]) {
157 filter
|= (1 << THREAD_READ
);
161 filter
|= (1 << THREAD_WRITE
);
165 filter
|= (1 << THREAD_TIMER
);
169 filter
|= (1 << THREAD_EVENT
);
173 filter
|= (1 << THREAD_EXECUTE
);
177 filter
|= (1 << THREAD_BACKGROUND
);
186 "Invalid filter \"%s\" specified,"
187 " must contain at least one of 'RWTEXB'%s",
188 argv
[idx_filter
]->arg
, VTY_NEWLINE
);
193 cpu_record_print(vty
, filter
);
197 static void cpu_record_hash_clear(struct hash_backet
*bucket
, void *args
)
199 thread_type
*filter
= args
;
200 struct cpu_thread_history
*a
= bucket
->data
;
202 if (!(a
->types
& *filter
))
205 hash_release(cpu_record
, bucket
->data
);
208 static void cpu_record_clear(thread_type filter
)
210 thread_type
*tmp
= &filter
;
211 hash_iterate(cpu_record
, (void (*)(struct hash_backet
*,
212 void *))cpu_record_hash_clear
,
216 DEFUN (clear_thread_cpu
,
217 clear_thread_cpu_cmd
,
218 "clear thread cpu [FILTER]",
219 "Clear stored data\n"
220 "Thread information\n"
222 "Display filter (rwtexb)\n")
226 thread_type filter
= (thread_type
)-1U;
230 while (argv
[idx_filter
]->arg
[i
] != '\0') {
231 switch (argv
[idx_filter
]->arg
[i
]) {
234 filter
|= (1 << THREAD_READ
);
238 filter
|= (1 << THREAD_WRITE
);
242 filter
|= (1 << THREAD_TIMER
);
246 filter
|= (1 << THREAD_EVENT
);
250 filter
|= (1 << THREAD_EXECUTE
);
254 filter
|= (1 << THREAD_BACKGROUND
);
263 "Invalid filter \"%s\" specified,"
264 " must contain at least one of 'RWTEXB'%s",
265 argv
[idx_filter
]->arg
, VTY_NEWLINE
);
270 cpu_record_clear(filter
);
274 void thread_cmd_init(void)
276 install_element(VIEW_NODE
, &show_thread_cpu_cmd
);
277 install_element(ENABLE_NODE
, &clear_thread_cpu_cmd
);
280 static int thread_timer_cmp(void *a
, void *b
)
282 struct thread
*thread_a
= a
;
283 struct thread
*thread_b
= b
;
285 if (timercmp(&thread_a
->u
.sands
, &thread_b
->u
.sands
, <))
287 if (timercmp(&thread_a
->u
.sands
, &thread_b
->u
.sands
, >))
292 static void thread_timer_update(void *node
, int actual_position
)
294 struct thread
*thread
= node
;
296 thread
->index
= actual_position
;
299 /* Allocate new thread master. */
300 struct thread_master
*thread_master_create(void)
302 struct thread_master
*rv
;
305 getrlimit(RLIMIT_NOFILE
, &limit
);
307 if (cpu_record
== NULL
)
308 cpu_record
= hash_create(
309 (unsigned int (*)(void *))cpu_record_hash_key
,
310 (int (*)(const void *,
311 const void *))cpu_record_hash_cmp
);
313 rv
= XCALLOC(MTYPE_THREAD_MASTER
, sizeof(struct thread_master
));
318 rv
->fd_limit
= (int)limit
.rlim_cur
;
320 XCALLOC(MTYPE_THREAD
, sizeof(struct thread
*) * rv
->fd_limit
);
321 if (rv
->read
== NULL
) {
322 XFREE(MTYPE_THREAD_MASTER
, rv
);
327 XCALLOC(MTYPE_THREAD
, sizeof(struct thread
*) * rv
->fd_limit
);
328 if (rv
->write
== NULL
) {
329 XFREE(MTYPE_THREAD
, rv
->read
);
330 XFREE(MTYPE_THREAD_MASTER
, rv
);
334 /* Initialize the timer queues */
335 rv
->timer
= pqueue_create();
336 rv
->background
= pqueue_create();
337 rv
->timer
->cmp
= rv
->background
->cmp
= thread_timer_cmp
;
338 rv
->timer
->update
= rv
->background
->update
= thread_timer_update
;
340 #if defined(HAVE_POLL_CALL)
341 rv
->handler
.pfdsize
= rv
->fd_limit
;
342 rv
->handler
.pfdcount
= 0;
343 rv
->handler
.pfds
= XCALLOC(MTYPE_THREAD_MASTER
,
344 sizeof(struct pollfd
) * rv
->handler
.pfdsize
);
349 /* Add a new thread to the list. */
350 static void thread_list_add(struct thread_list
*list
, struct thread
*thread
)
353 thread
->prev
= list
->tail
;
355 list
->tail
->next
= thread
;
362 /* Delete a thread from the list. */
363 static struct thread
*thread_list_delete(struct thread_list
*list
,
364 struct thread
*thread
)
367 thread
->next
->prev
= thread
->prev
;
369 list
->tail
= thread
->prev
;
371 thread
->prev
->next
= thread
->next
;
373 list
->head
= thread
->next
;
374 thread
->next
= thread
->prev
= NULL
;
379 static void thread_delete_fd(struct thread
**thread_array
,
380 struct thread
*thread
)
382 thread_array
[thread
->u
.fd
] = NULL
;
385 static void thread_add_fd(struct thread
**thread_array
, struct thread
*thread
)
387 thread_array
[thread
->u
.fd
] = thread
;
390 /* Thread list is empty or not. */
391 static int thread_empty(struct thread_list
*list
)
393 return list
->head
? 0 : 1;
396 /* Delete top of the list and return it. */
397 static struct thread
*thread_trim_head(struct thread_list
*list
)
399 if (!thread_empty(list
))
400 return thread_list_delete(list
, list
->head
);
404 /* Move thread to unuse list. */
405 static void thread_add_unuse(struct thread_master
*m
, struct thread
*thread
)
407 assert(m
!= NULL
&& thread
!= NULL
);
408 assert(thread
->next
== NULL
);
409 assert(thread
->prev
== NULL
);
411 thread
->type
= THREAD_UNUSED
;
412 thread
->hist
->total_active
--;
413 thread_list_add(&m
->unuse
, thread
);
416 /* Free all unused thread. */
417 static void thread_list_free(struct thread_master
*m
, struct thread_list
*list
)
422 for (t
= list
->head
; t
; t
= next
) {
424 XFREE(MTYPE_THREAD
, t
);
430 static void thread_array_free(struct thread_master
*m
,
431 struct thread
**thread_array
)
436 for (index
= 0; index
< m
->fd_limit
; ++index
) {
437 t
= thread_array
[index
];
439 thread_array
[index
] = NULL
;
440 XFREE(MTYPE_THREAD
, t
);
444 XFREE(MTYPE_THREAD
, thread_array
);
447 static void thread_queue_free(struct thread_master
*m
, struct pqueue
*queue
)
451 for (i
= 0; i
< queue
->size
; i
++)
452 XFREE(MTYPE_THREAD
, queue
->array
[i
]);
454 m
->alloc
-= queue
->size
;
455 pqueue_delete(queue
);
459 * thread_master_free_unused
461 * As threads are finished with they are put on the
462 * unuse list for later reuse.
463 * If we are shutting down, Free up unused threads
464 * So we can see if we forget to shut anything off
466 void thread_master_free_unused(struct thread_master
*m
)
469 while ((t
= thread_trim_head(&m
->unuse
)) != NULL
) {
470 XFREE(MTYPE_THREAD
, t
);
474 /* Stop thread scheduler. */
475 void thread_master_free(struct thread_master
*m
)
477 thread_array_free(m
, m
->read
);
478 thread_array_free(m
, m
->write
);
479 thread_queue_free(m
, m
->timer
);
480 thread_list_free(m
, &m
->event
);
481 thread_list_free(m
, &m
->ready
);
482 thread_list_free(m
, &m
->unuse
);
483 thread_queue_free(m
, m
->background
);
485 #if defined(HAVE_POLL_CALL)
486 XFREE(MTYPE_THREAD_MASTER
, m
->handler
.pfds
);
488 XFREE(MTYPE_THREAD_MASTER
, m
);
491 hash_clean(cpu_record
, cpu_record_hash_free
);
492 hash_free(cpu_record
);
497 /* Return remain time in second. */
498 unsigned long thread_timer_remain_second(struct thread
*thread
)
500 int64_t remain
= monotime_until(&thread
->u
.sands
, NULL
) / 1000000LL;
501 return remain
< 0 ? 0 : remain
;
504 #define debugargdef const char *funcname, const char *schedfrom, int fromln
505 #define debugargpass funcname, schedfrom, fromln
507 struct timeval
thread_timer_remain(struct thread
*thread
)
509 struct timeval remain
;
510 monotime_until(&thread
->u
.sands
, &remain
);
514 /* Get new thread. */
515 static struct thread
*thread_get(struct thread_master
*m
, u_char type
,
516 int (*func
)(struct thread
*), void *arg
,
519 struct thread
*thread
= thread_trim_head(&m
->unuse
);
520 struct cpu_thread_history tmp
;
523 thread
= XCALLOC(MTYPE_THREAD
, sizeof(struct thread
));
527 thread
->add_type
= type
;
531 thread
->yield
= THREAD_YIELD_TIME_SLOT
; /* default */
534 * So if the passed in funcname is not what we have
535 * stored that means the thread->hist needs to be
536 * updated. We keep the last one around in unused
537 * under the assumption that we are probably
538 * going to immediately allocate the same
540 * This hopefully saves us some serious
543 if (thread
->funcname
!= funcname
|| thread
->func
!= func
) {
545 tmp
.funcname
= funcname
;
547 hash_get(cpu_record
, &tmp
,
548 (void *(*)(void *))cpu_record_hash_alloc
);
550 thread
->hist
->total_active
++;
552 thread
->funcname
= funcname
;
553 thread
->schedfrom
= schedfrom
;
554 thread
->schedfrom_line
= fromln
;
559 #if defined(HAVE_POLL_CALL)
561 #define fd_copy_fd_set(X) (X)
563 /* generic add thread function */
564 static struct thread
*generic_thread_add(struct thread_master
*m
,
565 int (*func
)(struct thread
*),
566 void *arg
, int fd
, int dir
,
569 struct thread
*thread
;
574 if (dir
== THREAD_READ
) {
575 event
= (POLLIN
| POLLHUP
);
578 event
= (POLLOUT
| POLLHUP
);
582 nfds_t queuepos
= m
->handler
.pfdcount
;
584 for (i
= 0; i
< m
->handler
.pfdcount
; i
++)
585 if (m
->handler
.pfds
[i
].fd
== fd
) {
590 /* is there enough space for a new fd? */
591 assert(queuepos
< m
->handler
.pfdsize
);
593 thread
= thread_get(m
, type
, func
, arg
, debugargpass
);
594 m
->handler
.pfds
[queuepos
].fd
= fd
;
595 m
->handler
.pfds
[queuepos
].events
|= event
;
596 if (queuepos
== m
->handler
.pfdcount
)
597 m
->handler
.pfdcount
++;
603 #define fd_copy_fd_set(X) (X)
606 static int fd_select(struct thread_master
*m
, int size
, thread_fd_set
*read
,
607 thread_fd_set
*write
, thread_fd_set
*except
,
608 struct timeval
*timer_wait
)
611 #if defined(HAVE_POLL_CALL)
612 /* recalc timeout for poll. Attention NULL pointer is no timeout with
613 select, where with poll no timeount is -1 */
615 if (timer_wait
!= NULL
)
616 timeout
= (timer_wait
->tv_sec
* 1000)
617 + (timer_wait
->tv_usec
/ 1000);
619 num
= poll(m
->handler
.pfds
,
620 m
->handler
.pfdcount
+ m
->handler
.pfdcountsnmp
, timeout
);
622 num
= select(size
, read
, write
, except
, timer_wait
);
628 static int fd_is_set(struct thread
*thread
, thread_fd_set
*fdset
, int pos
)
630 #if defined(HAVE_POLL_CALL)
633 return FD_ISSET(THREAD_FD(thread
), fdset
);
637 static int fd_clear_read_write(struct thread
*thread
)
639 #if !defined(HAVE_POLL_CALL)
640 thread_fd_set
*fdset
= NULL
;
641 int fd
= THREAD_FD(thread
);
643 if (thread
->type
== THREAD_READ
)
644 fdset
= &thread
->master
->handler
.readfd
;
646 fdset
= &thread
->master
->handler
.writefd
;
648 if (!FD_ISSET(fd
, fdset
))
656 /* Add new read thread. */
657 struct thread
*funcname_thread_add_read_write(int dir
, struct thread_master
*m
,
658 int (*func
)(struct thread
*),
659 void *arg
, int fd
, debugargdef
)
661 struct thread
*thread
= NULL
;
663 #if !defined(HAVE_POLL_CALL)
664 thread_fd_set
*fdset
= NULL
;
665 if (dir
== THREAD_READ
)
666 fdset
= &m
->handler
.readfd
;
668 fdset
= &m
->handler
.writefd
;
671 #if defined(HAVE_POLL_CALL)
672 thread
= generic_thread_add(m
, func
, arg
, fd
, dir
, debugargpass
);
677 if (FD_ISSET(fd
, fdset
)) {
678 zlog_warn("There is already %s fd [%d]",
679 (dir
== THREAD_READ
) ? "read" : "write", fd
);
684 thread
= thread_get(m
, dir
, func
, arg
, debugargpass
);
688 if (dir
== THREAD_READ
)
689 thread_add_fd(m
->read
, thread
);
691 thread_add_fd(m
->write
, thread
);
696 static struct thread
*funcname_thread_add_timer_timeval(
697 struct thread_master
*m
, int (*func
)(struct thread
*), int type
,
698 void *arg
, struct timeval
*time_relative
, debugargdef
)
700 struct thread
*thread
;
701 struct pqueue
*queue
;
705 assert(type
== THREAD_TIMER
|| type
== THREAD_BACKGROUND
);
706 assert(time_relative
);
708 queue
= ((type
== THREAD_TIMER
) ? m
->timer
: m
->background
);
709 thread
= thread_get(m
, type
, func
, arg
, debugargpass
);
711 monotime(&thread
->u
.sands
);
712 timeradd(&thread
->u
.sands
, time_relative
, &thread
->u
.sands
);
714 pqueue_enqueue(thread
, queue
);
719 /* Add timer event thread. */
720 struct thread
*funcname_thread_add_timer(struct thread_master
*m
,
721 int (*func
)(struct thread
*),
722 void *arg
, long timer
, debugargdef
)
731 return funcname_thread_add_timer_timeval(m
, func
, THREAD_TIMER
, arg
,
732 &trel
, debugargpass
);
735 /* Add timer event thread with "millisecond" resolution */
736 struct thread
*funcname_thread_add_timer_msec(struct thread_master
*m
,
737 int (*func
)(struct thread
*),
738 void *arg
, long timer
,
745 trel
.tv_sec
= timer
/ 1000;
746 trel
.tv_usec
= 1000 * (timer
% 1000);
748 return funcname_thread_add_timer_timeval(m
, func
, THREAD_TIMER
, arg
,
749 &trel
, debugargpass
);
752 /* Add timer event thread with "millisecond" resolution */
753 struct thread
*funcname_thread_add_timer_tv(struct thread_master
*m
,
754 int (*func
)(struct thread
*),
755 void *arg
, struct timeval
*tv
,
758 return funcname_thread_add_timer_timeval(m
, func
, THREAD_TIMER
, arg
, tv
,
762 /* Add a background thread, with an optional millisec delay */
763 struct thread
*funcname_thread_add_background(struct thread_master
*m
,
764 int (*func
)(struct thread
*),
765 void *arg
, long delay
,
773 trel
.tv_sec
= delay
/ 1000;
774 trel
.tv_usec
= 1000 * (delay
% 1000);
780 return funcname_thread_add_timer_timeval(m
, func
, THREAD_BACKGROUND
,
781 arg
, &trel
, debugargpass
);
784 /* Add simple event thread. */
785 struct thread
*funcname_thread_add_event(struct thread_master
*m
,
786 int (*func
)(struct thread
*),
787 void *arg
, int val
, debugargdef
)
789 struct thread
*thread
;
793 thread
= thread_get(m
, THREAD_EVENT
, func
, arg
, debugargpass
);
795 thread_list_add(&m
->event
, thread
);
800 static void thread_cancel_read_or_write(struct thread
*thread
, short int state
)
802 #if defined(HAVE_POLL_CALL)
805 for (i
= 0; i
< thread
->master
->handler
.pfdcount
; ++i
)
806 if (thread
->master
->handler
.pfds
[i
].fd
== thread
->u
.fd
) {
807 thread
->master
->handler
.pfds
[i
].events
&= ~(state
);
809 /* remove thread fds from pfd list */
810 if (thread
->master
->handler
.pfds
[i
].events
== 0) {
811 memmove(thread
->master
->handler
.pfds
+ i
,
812 thread
->master
->handler
.pfds
+ i
+ 1,
813 (thread
->master
->handler
.pfdsize
- i
814 - 1) * sizeof(struct pollfd
));
815 thread
->master
->handler
.pfdcount
--;
821 fd_clear_read_write(thread
);
824 /* Cancel thread from scheduler. */
825 void thread_cancel(struct thread
*thread
)
827 struct thread_list
*list
= NULL
;
828 struct pqueue
*queue
= NULL
;
829 struct thread
**thread_array
= NULL
;
831 switch (thread
->type
) {
833 #if defined(HAVE_POLL_CALL)
834 thread_cancel_read_or_write(thread
, POLLIN
| POLLHUP
);
836 thread_cancel_read_or_write(thread
, 0);
838 thread_array
= thread
->master
->read
;
841 #if defined(HAVE_POLL_CALL)
842 thread_cancel_read_or_write(thread
, POLLOUT
| POLLHUP
);
844 thread_cancel_read_or_write(thread
, 0);
846 thread_array
= thread
->master
->write
;
849 queue
= thread
->master
->timer
;
852 list
= &thread
->master
->event
;
855 list
= &thread
->master
->ready
;
857 case THREAD_BACKGROUND
:
858 queue
= thread
->master
->background
;
866 assert(thread
->index
>= 0);
867 assert(thread
== queue
->array
[thread
->index
]);
868 pqueue_remove_at(thread
->index
, queue
);
870 thread_list_delete(list
, thread
);
871 } else if (thread_array
) {
872 thread_delete_fd(thread_array
, thread
);
874 assert(!"Thread should be either in queue or list or array!");
877 thread_add_unuse(thread
->master
, thread
);
880 /* Delete all events which has argument value arg. */
881 unsigned int thread_cancel_event(struct thread_master
*m
, void *arg
)
883 unsigned int ret
= 0;
884 struct thread
*thread
;
886 thread
= m
->event
.head
;
895 thread_list_delete(&m
->event
, t
);
896 thread_add_unuse(m
, t
);
900 /* thread can be on the ready list too */
901 thread
= m
->ready
.head
;
910 thread_list_delete(&m
->ready
, t
);
911 thread_add_unuse(m
, t
);
917 static struct timeval
*thread_timer_wait(struct pqueue
*queue
,
918 struct timeval
*timer_val
)
921 struct thread
*next_timer
= queue
->array
[0];
922 monotime_until(&next_timer
->u
.sands
, timer_val
);
928 static struct thread
*thread_run(struct thread_master
*m
, struct thread
*thread
,
929 struct thread
*fetch
)
932 thread_add_unuse(m
, thread
);
936 static int thread_process_fds_helper(struct thread_master
*m
,
937 struct thread
*thread
,
938 thread_fd_set
*fdset
, short int state
,
941 struct thread
**thread_array
;
946 if (thread
->type
== THREAD_READ
)
947 thread_array
= m
->read
;
949 thread_array
= m
->write
;
951 if (fd_is_set(thread
, fdset
, pos
)) {
952 fd_clear_read_write(thread
);
953 thread_delete_fd(thread_array
, thread
);
954 thread_list_add(&m
->ready
, thread
);
955 thread
->type
= THREAD_READY
;
956 #if defined(HAVE_POLL_CALL)
957 thread
->master
->handler
.pfds
[pos
].events
&= ~(state
);
964 #if defined(HAVE_POLL_CALL)
966 /* check poll events */
967 static void check_pollfds(struct thread_master
*m
, fd_set
*readfd
, int num
)
971 for (i
= 0; i
< m
->handler
.pfdcount
&& ready
< num
; ++i
) {
972 /* no event for current fd? immideatly continue */
973 if (m
->handler
.pfds
[i
].revents
== 0)
978 /* POLLIN / POLLOUT process event */
979 if (m
->handler
.pfds
[i
].revents
& (POLLIN
| POLLHUP
))
980 thread_process_fds_helper(
981 m
, m
->read
[m
->handler
.pfds
[i
].fd
], NULL
, POLLIN
,
983 if (m
->handler
.pfds
[i
].revents
& POLLOUT
)
984 thread_process_fds_helper(
985 m
, m
->write
[m
->handler
.pfds
[i
].fd
], NULL
,
988 /* remove fd from list on POLLNVAL */
989 if (m
->handler
.pfds
[i
].revents
& POLLNVAL
) {
990 memmove(m
->handler
.pfds
+ i
, m
->handler
.pfds
+ i
+ 1,
991 (m
->handler
.pfdsize
- i
- 1)
992 * sizeof(struct pollfd
));
993 m
->handler
.pfdcount
--;
996 m
->handler
.pfds
[i
].revents
= 0;
1001 static void thread_process_fds(struct thread_master
*m
, thread_fd_set
*rset
,
1002 thread_fd_set
*wset
, int num
)
1004 #if defined(HAVE_POLL_CALL)
1005 check_pollfds(m
, rset
, num
);
1007 int ready
= 0, index
;
1009 for (index
= 0; index
< m
->fd_limit
&& ready
< num
; ++index
) {
1010 ready
+= thread_process_fds_helper(m
, m
->read
[index
], rset
, 0,
1012 ready
+= thread_process_fds_helper(m
, m
->write
[index
], wset
, 0,
1018 /* Add all timers that have popped to the ready list. */
1019 static unsigned int thread_timer_process(struct pqueue
*queue
,
1020 struct timeval
*timenow
)
1022 struct thread
*thread
;
1023 unsigned int ready
= 0;
1025 while (queue
->size
) {
1026 thread
= queue
->array
[0];
1027 if (timercmp(timenow
, &thread
->u
.sands
, <))
1029 pqueue_dequeue(queue
);
1030 thread
->type
= THREAD_READY
;
1031 thread_list_add(&thread
->master
->ready
, thread
);
1037 /* process a list en masse, e.g. for event thread lists */
1038 static unsigned int thread_process(struct thread_list
*list
)
1040 struct thread
*thread
;
1041 struct thread
*next
;
1042 unsigned int ready
= 0;
1044 for (thread
= list
->head
; thread
; thread
= next
) {
1045 next
= thread
->next
;
1046 thread_list_delete(list
, thread
);
1047 thread
->type
= THREAD_READY
;
1048 thread_list_add(&thread
->master
->ready
, thread
);
1055 /* Fetch next ready thread. */
1056 struct thread
*thread_fetch(struct thread_master
*m
, struct thread
*fetch
)
1058 struct thread
*thread
;
1059 thread_fd_set readfd
;
1060 thread_fd_set writefd
;
1061 thread_fd_set exceptfd
;
1063 struct timeval timer_val
= {.tv_sec
= 0, .tv_usec
= 0};
1064 struct timeval timer_val_bg
;
1065 struct timeval
*timer_wait
= &timer_val
;
1066 struct timeval
*timer_wait_bg
;
1071 /* Signals pre-empt everything */
1072 quagga_sigevent_process();
1074 /* Drain the ready queue of already scheduled jobs, before
1078 if ((thread
= thread_trim_head(&m
->ready
)) != NULL
)
1079 return thread_run(m
, thread
, fetch
);
1081 /* To be fair to all kinds of threads, and avoid starvation, we
1082 * need to be careful to consider all thread types for
1084 * in each quanta. I.e. we should not return early from here on.
1087 /* Normal event are the next highest priority. */
1088 thread_process(&m
->event
);
1090 /* Structure copy. */
1091 #if !defined(HAVE_POLL_CALL)
1092 readfd
= fd_copy_fd_set(m
->handler
.readfd
);
1093 writefd
= fd_copy_fd_set(m
->handler
.writefd
);
1094 exceptfd
= fd_copy_fd_set(m
->handler
.exceptfd
);
1097 /* Calculate select wait timer if nothing else to do */
1098 if (m
->ready
.count
== 0) {
1099 timer_wait
= thread_timer_wait(m
->timer
, &timer_val
);
1101 thread_timer_wait(m
->background
, &timer_val_bg
);
1105 || (timercmp(timer_wait
, timer_wait_bg
, >))))
1106 timer_wait
= timer_wait_bg
;
1109 if (timer_wait
&& timer_wait
->tv_sec
< 0) {
1110 timerclear(&timer_val
);
1111 timer_wait
= &timer_val
;
1114 num
= fd_select(m
, FD_SETSIZE
, &readfd
, &writefd
, &exceptfd
,
1117 /* Signals should get quick treatment */
1120 continue; /* signal received - process it */
1121 zlog_warn("select() error: %s", safe_strerror(errno
));
1125 /* Check foreground timers. Historically, they have had higher
1126 priority than I/O threads, so let's push them onto the ready
1127 list in front of the I/O threads. */
1129 thread_timer_process(m
->timer
, &now
);
1131 /* Got IO, process it */
1133 thread_process_fds(m
, &readfd
, &writefd
, num
);
1136 /* If any threads were made ready above (I/O or foreground timer),
1137 perhaps we should avoid adding background timers to the ready
1138 list at this time. If this is code is uncommented, then background
1139 timer threads will not run unless there is nothing else to do. */
1140 if ((thread
= thread_trim_head (&m
->ready
)) != NULL
)
1141 return thread_run (m
, thread
, fetch
);
1144 /* Background timer/events, lowest priority */
1145 thread_timer_process(m
->background
, &now
);
1147 if ((thread
= thread_trim_head(&m
->ready
)) != NULL
)
1148 return thread_run(m
, thread
, fetch
);
1152 unsigned long thread_consumed_time(RUSAGE_T
*now
, RUSAGE_T
*start
,
1153 unsigned long *cputime
)
1155 /* This is 'user + sys' time. */
1156 *cputime
= timeval_elapsed(now
->cpu
.ru_utime
, start
->cpu
.ru_utime
)
1157 + timeval_elapsed(now
->cpu
.ru_stime
, start
->cpu
.ru_stime
);
1158 return timeval_elapsed(now
->real
, start
->real
);
1161 /* We should aim to yield after yield milliseconds, which defaults
1162 to THREAD_YIELD_TIME_SLOT .
1163 Note: we are using real (wall clock) time for this calculation.
1164 It could be argued that CPU time may make more sense in certain
1165 contexts. The things to consider are whether the thread may have
1166 blocked (in which case wall time increases, but CPU time does not),
1167 or whether the system is heavily loaded with other processes competing
1168 for CPU time. On balance, wall clock time seems to make sense.
1169 Plus it has the added benefit that gettimeofday should be faster
1170 than calling getrusage. */
1171 int thread_should_yield(struct thread
*thread
)
1173 return monotime_since(&thread
->real
, NULL
) > (int64_t)thread
->yield
;
1176 void thread_set_yield_time(struct thread
*thread
, unsigned long yield_time
)
1178 thread
->yield
= yield_time
;
1181 void thread_getrusage(RUSAGE_T
*r
)
1184 getrusage(RUSAGE_SELF
, &(r
->cpu
));
1187 struct thread
*thread_current
= NULL
;
1189 /* We check thread consumed time. If the system has getrusage, we'll
1190 use that to get in-depth stats on the performance of the thread in addition
1191 to wall clock time stats from gettimeofday. */
1192 void thread_call(struct thread
*thread
)
1194 unsigned long realtime
, cputime
;
1195 RUSAGE_T before
, after
;
1198 thread
->real
= before
.real
;
1200 thread_current
= thread
;
1201 (*thread
->func
)(thread
);
1202 thread_current
= NULL
;
1206 realtime
= thread_consumed_time(&after
, &before
, &cputime
);
1207 thread
->hist
->real
.total
+= realtime
;
1208 if (thread
->hist
->real
.max
< realtime
)
1209 thread
->hist
->real
.max
= realtime
;
1210 thread
->hist
->cpu
.total
+= cputime
;
1211 if (thread
->hist
->cpu
.max
< cputime
)
1212 thread
->hist
->cpu
.max
= cputime
;
1214 ++(thread
->hist
->total_calls
);
1215 thread
->hist
->types
|= (1 << thread
->add_type
);
1217 #ifdef CONSUMED_TIME_CHECK
1218 if (realtime
> CONSUMED_TIME_CHECK
) {
1220 * We have a CPU Hog on our hands.
1221 * Whinge about it now, so we're aware this is yet another task
1225 "SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
1226 thread
->funcname
, (unsigned long)thread
->func
,
1227 realtime
/ 1000, cputime
/ 1000);
1229 #endif /* CONSUMED_TIME_CHECK */
1232 /* Execute thread */
1233 struct thread
*funcname_thread_execute(struct thread_master
*m
,
1234 int (*func
)(struct thread
*), void *arg
,
1235 int val
, debugargdef
)
1237 struct cpu_thread_history tmp
;
1238 struct thread dummy
;
1240 memset(&dummy
, 0, sizeof(struct thread
));
1242 dummy
.type
= THREAD_EVENT
;
1243 dummy
.add_type
= THREAD_EXECUTE
;
1244 dummy
.master
= NULL
;
1248 tmp
.func
= dummy
.func
= func
;
1249 tmp
.funcname
= dummy
.funcname
= funcname
;
1250 dummy
.hist
= hash_get(cpu_record
, &tmp
,
1251 (void *(*)(void *))cpu_record_hash_alloc
);
1253 dummy
.schedfrom
= schedfrom
;
1254 dummy
.schedfrom_line
= fromln
;
1256 thread_call(&dummy
);