]>
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
30 /* Struct timeval's tv_usec one second value. */
31 #define TIMER_SECOND_MICRO 1000000L
34 timeval_adjust (struct timeval a
)
36 while (a
.tv_usec
>= TIMER_SECOND_MICRO
)
38 a
.tv_usec
-= TIMER_SECOND_MICRO
;
44 a
.tv_usec
+= TIMER_SECOND_MICRO
;
54 if (a
.tv_sec
> TIMER_SECOND_MICRO
)
55 a
.tv_sec
= TIMER_SECOND_MICRO
;
61 timeval_subtract (struct timeval a
, struct timeval b
)
65 ret
.tv_usec
= a
.tv_usec
- b
.tv_usec
;
66 ret
.tv_sec
= a
.tv_sec
- b
.tv_sec
;
68 return timeval_adjust (ret
);
72 timeval_cmp (struct timeval a
, struct timeval b
)
74 return (a
.tv_sec
== b
.tv_sec
75 ? a
.tv_usec
- b
.tv_usec
: a
.tv_sec
- b
.tv_sec
);
79 timeval_elapsed (struct timeval a
, struct timeval b
)
81 return (((a
.tv_sec
- b
.tv_sec
) * TIMER_SECOND_MICRO
)
82 + (a
.tv_usec
- b
.tv_usec
));
85 /* List allocation and head/tail print out. */
87 thread_list_debug (struct thread_list
*list
)
89 printf ("count [%d] head [%p] tail [%p]\n",
90 list
->count
, list
->head
, list
->tail
);
93 /* Debug print for thread_master. */
95 thread_master_debug (struct thread_master
*m
)
97 printf ("-----------\n");
98 printf ("readlist : ");
99 thread_list_debug (&m
->read
);
100 printf ("writelist : ");
101 thread_list_debug (&m
->write
);
102 printf ("timerlist : ");
103 thread_list_debug (&m
->timer
);
104 printf ("eventlist : ");
105 thread_list_debug (&m
->event
);
106 printf ("unuselist : ");
107 thread_list_debug (&m
->unuse
);
108 printf ("total alloc: [%ld]\n", m
->alloc
);
109 printf ("-----------\n");
112 /* Allocate new thread master. */
113 struct thread_master
*
114 thread_master_create ()
116 return (struct thread_master
*) XCALLOC (MTYPE_THREAD_MASTER
,
117 sizeof (struct thread_master
));
120 /* Add a new thread to the list. */
122 thread_list_add (struct thread_list
*list
, struct thread
*thread
)
125 thread
->prev
= list
->tail
;
127 list
->tail
->next
= thread
;
134 /* Add a new thread just before the point. */
136 thread_list_add_before (struct thread_list
*list
,
137 struct thread
*point
,
138 struct thread
*thread
)
140 thread
->next
= point
;
141 thread
->prev
= point
->prev
;
143 point
->prev
->next
= thread
;
146 point
->prev
= thread
;
150 /* Delete a thread from the list. */
151 static struct thread
*
152 thread_list_delete (struct thread_list
*list
, struct thread
*thread
)
155 thread
->next
->prev
= thread
->prev
;
157 list
->tail
= thread
->prev
;
159 thread
->prev
->next
= thread
->next
;
161 list
->head
= thread
->next
;
162 thread
->next
= thread
->prev
= NULL
;
167 /* Move thread to unuse list. */
169 thread_add_unuse (struct thread_master
*m
, struct thread
*thread
)
172 assert (thread
->next
== NULL
);
173 assert (thread
->prev
== NULL
);
174 assert (thread
->type
== THREAD_UNUSED
);
175 thread_list_add (&m
->unuse
, thread
);
178 /* Free all unused thread. */
180 thread_list_free (struct thread_master
*m
, struct thread_list
*list
)
185 for (t
= list
->head
; t
; t
= next
)
188 XFREE (MTYPE_THREAD
, t
);
194 /* Stop thread scheduler. */
196 thread_master_free (struct thread_master
*m
)
198 thread_list_free (m
, &m
->read
);
199 thread_list_free (m
, &m
->write
);
200 thread_list_free (m
, &m
->timer
);
201 thread_list_free (m
, &m
->event
);
202 thread_list_free (m
, &m
->ready
);
203 thread_list_free (m
, &m
->unuse
);
205 XFREE (MTYPE_THREAD_MASTER
, m
);
208 /* Delete top of the list and return it. */
209 static struct thread
*
210 thread_trim_head (struct thread_list
*list
)
213 return thread_list_delete (list
, list
->head
);
217 /* Thread list is empty or not. */
219 thread_empty (struct thread_list
*list
)
221 return list
->head
? 0 : 1;
224 /* Return remain time in second. */
226 thread_timer_remain_second (struct thread
*thread
)
228 struct timeval timer_now
;
230 gettimeofday (&timer_now
, NULL
);
232 if (thread
->u
.sands
.tv_sec
- timer_now
.tv_sec
> 0)
233 return thread
->u
.sands
.tv_sec
- timer_now
.tv_sec
;
238 /* Get new thread. */
239 static struct thread
*
240 thread_get (struct thread_master
*m
, u_char type
,
241 int (*func
) (struct thread
*), void *arg
)
243 struct thread
*thread
;
246 thread
= thread_trim_head (&m
->unuse
);
249 thread
= XCALLOC (MTYPE_THREAD
, sizeof (struct thread
));
260 /* Add new read thread. */
262 thread_add_read (struct thread_master
*m
,
263 int (*func
) (struct thread
*), void *arg
, int fd
)
265 struct thread
*thread
;
269 if (FD_ISSET (fd
, &m
->readfd
))
271 zlog (NULL
, LOG_WARNING
, "There is already read fd [%d]", fd
);
275 thread
= thread_get (m
, THREAD_READ
, func
, arg
);
276 FD_SET (fd
, &m
->readfd
);
278 thread_list_add (&m
->read
, thread
);
283 /* Add new write thread. */
285 thread_add_write (struct thread_master
*m
,
286 int (*func
) (struct thread
*), void *arg
, int fd
)
288 struct thread
*thread
;
292 if (FD_ISSET (fd
, &m
->writefd
))
294 zlog (NULL
, LOG_WARNING
, "There is already write fd [%d]", fd
);
298 thread
= thread_get (m
, THREAD_WRITE
, func
, arg
);
299 FD_SET (fd
, &m
->writefd
);
301 thread_list_add (&m
->write
, thread
);
306 /* Add timer event thread. */
308 thread_add_timer (struct thread_master
*m
,
309 int (*func
) (struct thread
*), void *arg
, long timer
)
311 struct timeval timer_now
;
312 struct thread
*thread
;
313 #ifndef TIMER_NO_SORT
315 #endif /* TIMER_NO_SORT */
319 thread
= thread_get (m
, THREAD_TIMER
, func
, arg
);
321 /* Do we need jitter here? */
322 gettimeofday (&timer_now
, NULL
);
323 timer_now
.tv_sec
+= timer
;
324 thread
->u
.sands
= timer_now
;
326 /* Sort by timeval. */
328 thread_list_add (&m
->timer
, thread
);
330 for (tt
= m
->timer
.head
; tt
; tt
= tt
->next
)
331 if (timeval_cmp (thread
->u
.sands
, tt
->u
.sands
) <= 0)
335 thread_list_add_before (&m
->timer
, tt
, thread
);
337 thread_list_add (&m
->timer
, thread
);
338 #endif /* TIMER_NO_SORT */
343 /* Add simple event thread. */
345 thread_add_event (struct thread_master
*m
,
346 int (*func
) (struct thread
*), void *arg
, int val
)
348 struct thread
*thread
;
352 thread
= thread_get (m
, THREAD_EVENT
, func
, arg
);
354 thread_list_add (&m
->event
, thread
);
359 /* Cancel thread from scheduler. */
361 thread_cancel (struct thread
*thread
)
363 switch (thread
->type
)
366 assert (FD_ISSET (thread
->u
.fd
, &thread
->master
->readfd
));
367 FD_CLR (thread
->u
.fd
, &thread
->master
->readfd
);
368 thread_list_delete (&thread
->master
->read
, thread
);
371 assert (FD_ISSET (thread
->u
.fd
, &thread
->master
->writefd
));
372 FD_CLR (thread
->u
.fd
, &thread
->master
->writefd
);
373 thread_list_delete (&thread
->master
->write
, thread
);
376 thread_list_delete (&thread
->master
->timer
, thread
);
379 thread_list_delete (&thread
->master
->event
, thread
);
382 thread_list_delete (&thread
->master
->ready
, thread
);
387 thread
->type
= THREAD_UNUSED
;
388 thread_add_unuse (thread
->master
, thread
);
391 /* Delete all events which has argument value arg. */
393 thread_cancel_event (struct thread_master
*m
, void *arg
)
395 struct thread
*thread
;
397 thread
= m
->event
.head
;
407 thread_list_delete (&m
->event
, t
);
408 t
->type
= THREAD_UNUSED
;
409 thread_add_unuse (m
, t
);
416 thread_timer_wait (struct thread_master
*m
, struct timeval
*timer_val
)
418 struct timeval timer_now
;
419 struct timeval timer_min
;
420 struct timeval
*timer_wait
;
422 gettimeofday (&timer_now
, NULL
);
425 for (thread
= m
->timer
.head
; thread
; thread
= thread
->next
)
428 timer_wait
= &thread
->u
.sands
;
429 else if (timeval_cmp (thread
->u
.sands
, *timer_wait
) < 0)
430 timer_wait
= &thread
->u
.sands
;
435 timer_min
= *timer_wait
;
436 timer_min
= timeval_subtract (timer_min
, timer_now
);
437 if (timer_min
.tv_sec
< 0)
439 timer_min
.tv_sec
= 0;
440 timer_min
.tv_usec
= 10;
442 timer_wait
= &timer_min
;
449 *timer_val
= timer_wait
;
454 #else /* ! TIMER_NO_SORT */
456 thread_timer_wait (struct thread_master
*m
, struct timeval
*timer_val
)
458 struct timeval timer_now
;
459 struct timeval timer_min
;
463 gettimeofday (&timer_now
, NULL
);
464 timer_min
= m
->timer
.head
->u
.sands
;
465 timer_min
= timeval_subtract (timer_min
, timer_now
);
466 if (timer_min
.tv_sec
< 0)
468 timer_min
.tv_sec
= 0;
469 timer_min
.tv_usec
= 10;
471 *timer_val
= timer_min
;
476 #endif /* TIMER_NO_SORT */
479 thread_run (struct thread_master
*m
, struct thread
*thread
,
480 struct thread
*fetch
)
483 thread
->type
= THREAD_UNUSED
;
484 thread_add_unuse (m
, thread
);
489 thread_process_fd (struct thread_master
*m
, struct thread_list
*list
,
490 fd_set
*fdset
, fd_set
*mfdset
)
492 struct thread
*thread
;
496 for (thread
= list
->head
; thread
; thread
= next
)
500 if (FD_ISSET (THREAD_FD (thread
), fdset
))
502 assert (FD_ISSET (THREAD_FD (thread
), mfdset
));
503 FD_CLR(THREAD_FD (thread
), mfdset
);
504 thread_list_delete (list
, thread
);
505 thread_list_add (&m
->ready
, thread
);
506 thread
->type
= THREAD_READY
;
513 /* Fetch next ready thread. */
515 thread_fetch (struct thread_master
*m
, struct thread
*fetch
)
519 struct thread
*thread
;
523 struct timeval timer_now
;
524 struct timeval timer_val
;
525 struct timeval
*timer_wait
;
526 struct timeval timer_nowait
;
528 timer_nowait
.tv_sec
= 0;
529 timer_nowait
.tv_usec
= 0;
533 /* Normal event is the highest priority. */
534 if ((thread
= thread_trim_head (&m
->event
)) != NULL
)
535 return thread_run (m
, thread
, fetch
);
538 gettimeofday (&timer_now
, NULL
);
540 for (thread
= m
->timer
.head
; thread
; thread
= thread
->next
)
541 if (timeval_cmp (timer_now
, thread
->u
.sands
) >= 0)
543 thread_list_delete (&m
->timer
, thread
);
544 return thread_run (m
, thread
, fetch
);
547 /* If there are any ready threads, process top of them. */
548 if ((thread
= thread_trim_head (&m
->ready
)) != NULL
)
549 return thread_run (m
, thread
, fetch
);
551 /* Structure copy. */
553 writefd
= m
->writefd
;
554 exceptfd
= m
->exceptfd
;
556 /* Calculate select wait timer. */
557 timer_wait
= thread_timer_wait (m
, &timer_val
);
559 num
= select (FD_SETSIZE
, &readfd
, &writefd
, &exceptfd
, timer_wait
);
569 zlog_warn ("select() error: %s", strerror (errno
));
573 /* Normal priority read thead. */
574 ready
= thread_process_fd (m
, &m
->read
, &readfd
, &m
->readfd
);
577 ready
= thread_process_fd (m
, &m
->write
, &writefd
, &m
->writefd
);
579 if ((thread
= thread_trim_head (&m
->ready
)) != NULL
)
580 return thread_run (m
, thread
, fetch
);
585 thread_consumed_time (RUSAGE_T
*now
, RUSAGE_T
*start
)
587 unsigned long thread_time
;
590 /* This is 'user + sys' time. */
591 thread_time
= timeval_elapsed (now
->ru_utime
, start
->ru_utime
);
592 thread_time
+= timeval_elapsed (now
->ru_stime
, start
->ru_stime
);
594 /* When rusage is not available, simple elapsed time is used. */
595 thread_time
= timeval_elapsed (*now
, *start
);
596 #endif /* HAVE_RUSAGE */
601 /* We should aim to yield after THREAD_YIELD_TIME_SLOT
604 thread_should_yield (struct thread
*thread
)
610 if (thread_consumed_time (&ru
, &thread
->ru
) > THREAD_YIELD_TIME_SLOT
)
616 /* We check thread consumed time. If the system has getrusage, we'll
617 use that to get indepth stats on the performance of the thread. If
618 not - we'll use gettimeofday for some guestimation. */
620 thread_call (struct thread
*thread
)
622 unsigned long thread_time
;
625 GETRUSAGE (&thread
->ru
);
627 (*thread
->func
) (thread
);
631 thread_time
= thread_consumed_time (&ru
, &thread
->ru
);
633 #ifdef THREAD_CONSUMED_TIME_CHECK
634 if (thread_time
> 200000L)
637 * We have a CPU Hog on our hands.
638 * Whinge about it now, so we're aware this is yet another task
641 zlog_err ("CPU HOG task %lx ran for %ldms",
642 /* FIXME: report the name of the function somehow */
643 (unsigned long) thread
->func
,
644 thread_time
/ 1000L);
646 #endif /* THREAD_CONSUMED_TIME_CHECK */
651 thread_execute (struct thread_master
*m
,
652 int (*func
)(struct thread
*),
658 memset (&dummy
, 0, sizeof (struct thread
));
660 dummy
.type
= THREAD_EVENT
;
665 thread_call (&dummy
);