2 * Copyright (c) 1993-1995, 1999-2000 Apple Computer, Inc.
5 * @APPLE_LICENSE_HEADER_START@
7 * The contents of this file constitute Original Code as defined in and
8 * are subject to the Apple Public Source License Version 1.1 (the
9 * "License"). You may not use this file except in compliance with the
10 * License. Please obtain a copy of the License at
11 * http://www.apple.com/publicsource and read it before using this file.
13 * This Original Code and all software distributed under the License are
14 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
18 * License for the specific language governing rights and limitations
21 * @APPLE_LICENSE_HEADER_END@
24 * Thread-based callout module.
29 * Pulled into Mac OS X (microkernel).
35 #include <mach/mach_types.h>
37 #include <kern/sched_prim.h>
38 #include <kern/clock.h>
39 #include <kern/task.h>
40 #include <kern/thread.h>
42 #include <kern/thread_call.h>
43 #include <kern/call_entry.h>
45 #include <kern/timer_call.h>
47 #define internal_call_num 768
49 #define thread_call_thread_min 4
53 internal_call_storage
[internal_call_num
];
55 decl_simple_lock_data(static,thread_call_lock
)
59 thread_call_delayed_timers
[NCPUS
];
63 internal_call_free_queue
,
64 pending_call_queue
, delayed_call_queue
;
76 activate_thread_awake
;
92 thread_call_initialized
= FALSE
;
94 static __inline__ thread_call_t
95 _internal_call_allocate(void);
97 static __inline__
void
98 _internal_call_release(
102 static __inline__
void
103 _pending_call_enqueue(
106 _pending_call_dequeue(
109 _delayed_call_enqueue(
112 _delayed_call_dequeue(
116 static void __inline__
117 _set_delayed_call_timer(
122 _remove_from_pending_queue(
123 thread_call_func_t func
,
124 thread_call_param_t param0
,
127 _remove_from_delayed_queue(
128 thread_call_func_t func
,
129 thread_call_param_t param0
,
133 static __inline__
void
134 _call_thread_wake(void);
138 _activate_thread(void);
142 timer_call_param_t p0
,
143 timer_call_param_t p1
146 #define qe(x) ((queue_entry_t)(x))
147 #define TC(x) ((thread_call_t)(x))
150 * Routine: thread_call_initialize [public]
152 * Description: Initialize this module, called
153 * early during system initialization.
155 * Preconditions: None.
157 * Postconditions: None.
161 thread_call_initialize(void)
167 if (thread_call_initialized
)
168 panic("thread_call_initialize");
170 simple_lock_init(&thread_call_lock
, ETAP_MISC_TIMER
);
173 simple_lock(&thread_call_lock
);
175 queue_init(&pending_call_queue
);
176 queue_init(&delayed_call_queue
);
178 queue_init(&internal_call_free_queue
);
180 call
= internal_call_storage
;
181 call
< &internal_call_storage
[internal_call_num
];
184 enqueue_tail(&internal_call_free_queue
, qe(call
));
187 for (i
= 0; i
< NCPUS
; i
++) {
188 timer_call_setup(&thread_call_delayed_timers
[i
],
189 _delayed_call_timer
, NULL
);
192 queue_init(&idle_thread_queue
);
193 thread_calls
.thread_lowat
= thread_call_thread_min
;
195 activate_thread_awake
= TRUE
;
196 thread_call_initialized
= TRUE
;
198 simple_unlock(&thread_call_lock
);
201 activate_thread
= kernel_thread_with_priority(
202 kernel_task
, MAXPRI_KERNEL
- 2,
203 _activate_thread
, TRUE
, TRUE
);
209 thread_call_func_t func
,
210 thread_call_param_t param0
213 call_entry_setup(call
, func
, param0
);
217 * Routine: _internal_call_allocate [private, inline]
219 * Purpose: Allocate an internal callout entry.
221 * Preconditions: thread_call_lock held.
223 * Postconditions: None.
226 static __inline__ thread_call_t
227 _internal_call_allocate(void)
231 if (queue_empty(&internal_call_free_queue
))
232 panic("_internal_call_allocate");
234 call
= TC(dequeue_head(&internal_call_free_queue
));
240 * Routine: _internal_call_release [private, inline]
242 * Purpose: Release an internal callout entry which
243 * is no longer pending (or delayed).
245 * Preconditions: thread_call_lock held.
247 * Postconditions: None.
252 _internal_call_release(
256 if ( call
>= internal_call_storage
&&
257 call
< &internal_call_storage
[internal_call_num
] )
258 enqueue_tail(&internal_call_free_queue
, qe(call
));
262 * Routine: _pending_call_enqueue [private, inline]
264 * Purpose: Place an entry at the end of the
265 * pending queue, to be executed soon.
267 * Preconditions: thread_call_lock held.
269 * Postconditions: None.
274 _pending_call_enqueue(
278 enqueue_tail(&pending_call_queue
, qe(call
));
279 if (++thread_calls
.pending_num
> thread_calls
.pending_hiwat
)
280 thread_calls
.pending_hiwat
= thread_calls
.pending_num
;
282 call
->state
= PENDING
;
286 * Routine: _pending_call_dequeue [private, inline]
288 * Purpose: Remove an entry from the pending queue,
289 * effectively unscheduling it.
291 * Preconditions: thread_call_lock held.
293 * Postconditions: None.
298 _pending_call_dequeue(
302 (void)remque(qe(call
));
303 thread_calls
.pending_num
--;
309 * Routine: _delayed_call_enqueue [private, inline]
311 * Purpose: Place an entry on the delayed queue,
312 * after existing entries with an earlier
313 * (or identical) deadline.
315 * Preconditions: thread_call_lock held.
317 * Postconditions: None.
322 _delayed_call_enqueue(
326 thread_call_t current
;
328 current
= TC(queue_first(&delayed_call_queue
));
331 if ( queue_end(&delayed_call_queue
, qe(current
)) ||
332 call
->deadline
< current
->deadline
) {
333 current
= TC(queue_prev(qe(current
)));
337 current
= TC(queue_next(qe(current
)));
340 insque(qe(call
), qe(current
));
341 if (++thread_calls
.delayed_num
> thread_calls
.delayed_hiwat
)
342 thread_calls
.delayed_hiwat
= thread_calls
.delayed_num
;
344 call
->state
= DELAYED
;
348 * Routine: _delayed_call_dequeue [private, inline]
350 * Purpose: Remove an entry from the delayed queue,
351 * effectively unscheduling it.
353 * Preconditions: thread_call_lock held.
355 * Postconditions: None.
360 _delayed_call_dequeue(
364 (void)remque(qe(call
));
365 thread_calls
.delayed_num
--;
371 * Routine: _set_delayed_call_timer [private]
373 * Purpose: Reset the timer so that it
374 * next expires when the entry is due.
376 * Preconditions: thread_call_lock held.
378 * Postconditions: None.
381 static __inline__
void
382 _set_delayed_call_timer(
386 timer_call_t timer
= &thread_call_delayed_timers
[cpu_number()];
388 timer_call_enter(timer
, call
->deadline
);
392 * Routine: _remove_from_pending_queue [private]
394 * Purpose: Remove the first (or all) matching
395 * entries from the pending queue,
396 * effectively unscheduling them.
397 * Returns whether any matching entries
400 * Preconditions: thread_call_lock held.
402 * Postconditions: None.
407 _remove_from_pending_queue(
408 thread_call_func_t func
,
409 thread_call_param_t param0
,
413 boolean_t call_removed
= FALSE
;
416 call
= TC(queue_first(&pending_call_queue
));
418 while (!queue_end(&pending_call_queue
, qe(call
))) {
419 if ( call
->func
== func
&&
420 call
->param0
== param0
) {
421 thread_call_t next
= TC(queue_next(qe(call
)));
423 _pending_call_dequeue(call
);
425 _internal_call_release(call
);
434 call
= TC(queue_next(qe(call
)));
437 return (call_removed
);
441 * Routine: _remove_from_delayed_queue [private]
443 * Purpose: Remove the first (or all) matching
444 * entries from the delayed queue,
445 * effectively unscheduling them.
446 * Returns whether any matching entries
449 * Preconditions: thread_call_lock held.
451 * Postconditions: None.
456 _remove_from_delayed_queue(
457 thread_call_func_t func
,
458 thread_call_param_t param0
,
462 boolean_t call_removed
= FALSE
;
465 call
= TC(queue_first(&delayed_call_queue
));
467 while (!queue_end(&delayed_call_queue
, qe(call
))) {
468 if ( call
->func
== func
&&
469 call
->param0
== param0
) {
470 thread_call_t next
= TC(queue_next(qe(call
)));
472 _delayed_call_dequeue(call
);
474 _internal_call_release(call
);
483 call
= TC(queue_next(qe(call
)));
486 return (call_removed
);
490 * Routine: thread_call_func [public]
492 * Purpose: Schedule a function callout.
493 * Guarantees { function, argument }
494 * uniqueness if unique_call is TRUE.
496 * Preconditions: Callable from an interrupt context
499 * Postconditions: None.
504 thread_call_func_t func
,
505 thread_call_param_t param
,
506 boolean_t unique_call
512 if (!thread_call_initialized
)
513 panic("thread_call_func");
516 simple_lock(&thread_call_lock
);
518 call
= TC(queue_first(&pending_call_queue
));
520 while (unique_call
&& !queue_end(&pending_call_queue
, qe(call
))) {
521 if ( call
->func
== func
&&
522 call
->param0
== param
) {
526 call
= TC(queue_next(qe(call
)));
529 if (!unique_call
|| queue_end(&pending_call_queue
, qe(call
))) {
530 call
= _internal_call_allocate();
532 call
->param0
= param
;
535 _pending_call_enqueue(call
);
540 simple_unlock(&thread_call_lock
);
545 * Routine: thread_call_func_delayed [public]
547 * Purpose: Schedule a function callout to
548 * occur at the stated time.
550 * Preconditions: Callable from an interrupt context
553 * Postconditions: None.
557 thread_call_func_delayed(
558 thread_call_func_t func
,
559 thread_call_param_t param
,
566 if (!thread_call_initialized
)
567 panic("thread_call_func_delayed");
570 simple_lock(&thread_call_lock
);
572 call
= _internal_call_allocate();
574 call
->param0
= param
;
576 call
->deadline
= deadline
;
578 _delayed_call_enqueue(call
);
580 if (queue_first(&delayed_call_queue
) == qe(call
))
581 _set_delayed_call_timer(call
);
583 simple_unlock(&thread_call_lock
);
588 * Routine: thread_call_func_cancel [public]
590 * Purpose: Unschedule a function callout.
591 * Removes one (or all)
592 * { function, argument }
593 * instance(s) from either (or both)
594 * the pending and the delayed queue,
595 * in that order. Returns a boolean
596 * indicating whether any calls were
599 * Preconditions: Callable from an interrupt context
602 * Postconditions: None.
606 thread_call_func_cancel(
607 thread_call_func_t func
,
608 thread_call_param_t param
,
616 simple_lock(&thread_call_lock
);
619 result
= _remove_from_pending_queue(func
, param
, cancel_all
) |
620 _remove_from_delayed_queue(func
, param
, cancel_all
);
622 result
= _remove_from_pending_queue(func
, param
, cancel_all
) ||
623 _remove_from_delayed_queue(func
, param
, cancel_all
);
625 simple_unlock(&thread_call_lock
);
632 * Routine: thread_call_allocate [public]
634 * Purpose: Allocate an external callout
637 * Preconditions: None.
639 * Postconditions: None.
643 thread_call_allocate(
644 thread_call_func_t func
,
645 thread_call_param_t param0
648 thread_call_t call
= (void *)kalloc(sizeof (thread_call_data_t
));
651 call
->param0
= param0
;
658 * Routine: thread_call_free [public]
660 * Purpose: Free an external callout
663 * Preconditions: None.
665 * Postconditions: None.
676 simple_lock(&thread_call_lock
);
678 if (call
->state
!= IDLE
) {
679 simple_unlock(&thread_call_lock
);
685 simple_unlock(&thread_call_lock
);
688 kfree((vm_offset_t
)call
, sizeof (thread_call_data_t
));
694 * Routine: thread_call_enter [public]
696 * Purpose: Schedule an external callout
697 * entry to occur "soon". Returns a
698 * boolean indicating whether the call
699 * had been already scheduled.
701 * Preconditions: Callable from an interrupt context
704 * Postconditions: None.
712 boolean_t result
= TRUE
;
716 simple_lock(&thread_call_lock
);
718 if (call
->state
!= PENDING
) {
719 if (call
->state
== DELAYED
)
720 _delayed_call_dequeue(call
);
721 else if (call
->state
== IDLE
)
724 _pending_call_enqueue(call
);
731 simple_unlock(&thread_call_lock
);
740 thread_call_param_t param1
743 boolean_t result
= TRUE
;
747 simple_lock(&thread_call_lock
);
749 if (call
->state
!= PENDING
) {
750 if (call
->state
== DELAYED
)
751 _delayed_call_dequeue(call
);
752 else if (call
->state
== IDLE
)
755 _pending_call_enqueue(call
);
760 call
->param1
= param1
;
762 simple_unlock(&thread_call_lock
);
769 * Routine: thread_call_enter_delayed [public]
771 * Purpose: Schedule an external callout
772 * entry to occur at the stated time.
773 * Returns a boolean indicating whether
774 * the call had been already scheduled.
776 * Preconditions: Callable from an interrupt context
779 * Postconditions: None.
783 thread_call_enter_delayed(
788 boolean_t result
= TRUE
;
792 simple_lock(&thread_call_lock
);
794 if (call
->state
== PENDING
)
795 _pending_call_dequeue(call
);
796 else if (call
->state
== DELAYED
)
797 _delayed_call_dequeue(call
);
798 else if (call
->state
== IDLE
)
802 call
->deadline
= deadline
;
804 _delayed_call_enqueue(call
);
806 if (queue_first(&delayed_call_queue
) == qe(call
))
807 _set_delayed_call_timer(call
);
809 simple_unlock(&thread_call_lock
);
816 thread_call_enter1_delayed(
818 thread_call_param_t param1
,
822 boolean_t result
= TRUE
;
826 simple_lock(&thread_call_lock
);
828 if (call
->state
== PENDING
)
829 _pending_call_dequeue(call
);
830 else if (call
->state
== DELAYED
)
831 _delayed_call_dequeue(call
);
832 else if (call
->state
== IDLE
)
835 call
->param1
= param1
;
836 call
->deadline
= deadline
;
838 _delayed_call_enqueue(call
);
840 if (queue_first(&delayed_call_queue
) == qe(call
))
841 _set_delayed_call_timer(call
);
843 simple_unlock(&thread_call_lock
);
850 * Routine: thread_call_cancel [public]
852 * Purpose: Unschedule a callout entry.
853 * Returns a boolean indicating
854 * whether the call had actually
857 * Preconditions: Callable from an interrupt context
860 * Postconditions: None.
868 boolean_t result
= TRUE
;
872 simple_lock(&thread_call_lock
);
874 if (call
->state
== PENDING
)
875 _pending_call_dequeue(call
);
876 else if (call
->state
== DELAYED
)
877 _delayed_call_dequeue(call
);
881 simple_unlock(&thread_call_lock
);
888 * Routine: thread_call_is_delayed [public]
890 * Purpose: Returns a boolean indicating
891 * whether a call is currently scheduled
892 * to occur at a later time. Optionally
893 * returns the expiration time.
895 * Preconditions: Callable from an interrupt context
898 * Postconditions: None.
902 thread_call_is_delayed(
906 boolean_t result
= FALSE
;
910 simple_lock(&thread_call_lock
);
912 if (call
->state
== DELAYED
) {
913 if (deadline
!= NULL
)
914 *deadline
= call
->deadline
;
918 simple_unlock(&thread_call_lock
);
925 * Routine: _call_thread_wake [private]
927 * Purpose: Wake a callout thread to service
928 * newly pending callout entries. May wake
929 * the activate thread to either wake or
930 * create additional callout threads.
932 * Preconditions: thread_call_lock held.
934 * Postconditions: None.
939 _call_thread_wake(void)
941 thread_t thread_to_wake
;
943 if (!queue_empty(&idle_thread_queue
)) {
945 &idle_thread_queue
, thread_to_wake
, thread_t
, wait_link
);
946 clear_wait(thread_to_wake
, THREAD_AWAKENED
);
947 thread_calls
.idle_thread_num
--;
950 thread_to_wake
= THREAD_NULL
;
952 if (!activate_thread_awake
&&
953 (thread_to_wake
== THREAD_NULL
|| thread_calls
.thread_num
<
954 (thread_calls
.active_num
+ thread_calls
.pending_num
))) {
955 clear_wait(activate_thread
, THREAD_AWAKENED
);
956 activate_thread_awake
= TRUE
;
960 #if defined (__i386__)
961 #define NO_CONTINUATIONS (1)
963 #define NO_CONTINUATIONS (0)
967 * Routine: _call_thread [private]
969 * Purpose: Executed by a callout thread.
971 * Preconditions: None.
973 * Postconditions: None.
978 _call_thread_continue(void)
980 thread_t self
= current_thread();
986 simple_lock(&thread_call_lock
);
988 while (thread_calls
.pending_num
> 0) {
990 thread_call_func_t func
;
991 thread_call_param_t param0
, param1
;
993 call
= TC(dequeue_head(&pending_call_queue
));
994 thread_calls
.pending_num
--;
997 param0
= call
->param0
;
998 param1
= call
->param1
;
1002 _internal_call_release(call
);
1004 if (++thread_calls
.active_num
> thread_calls
.active_hiwat
)
1005 thread_calls
.active_hiwat
= thread_calls
.active_num
;
1007 if (thread_calls
.pending_num
> 0)
1008 _call_thread_wake();
1010 simple_unlock(&thread_call_lock
);
1013 (*func
)(param0
, param1
);
1015 (void)thread_funnel_set(self
->funnel_lock
, FALSE
);
1018 simple_lock(&thread_call_lock
);
1020 thread_calls
.active_num
--;
1023 if ((thread_calls
.thread_num
- thread_calls
.active_num
) <=
1024 thread_calls
.thread_lowat
) {
1025 queue_enter(&idle_thread_queue
, self
, thread_t
, wait_link
);
1026 thread_calls
.idle_thread_num
++;
1028 assert_wait(&idle_thread_queue
, THREAD_INTERRUPTIBLE
);
1030 simple_unlock(&thread_call_lock
);
1033 #if NO_CONTINUATIONS
1034 thread_block((void (*)(void)) 0);
1037 thread_block(_call_thread_continue
);
1042 thread_calls
.thread_num
--;
1044 simple_unlock(&thread_call_lock
);
1047 (void) thread_terminate(self
->top_act
);
1055 thread_t self
= current_thread();
1057 stack_privilege(self
);
1059 _call_thread_continue();
1064 * Routine: _activate_thread [private]
1066 * Purpose: Executed by the activate thread.
1068 * Preconditions: None.
1070 * Postconditions: Never terminates.
1075 _activate_thread_continue(void)
1077 #if NO_CONTINUATIONS
1081 simple_lock(&thread_call_lock
);
1083 if (thread_calls
.thread_num
<
1084 (thread_calls
.active_num
+ thread_calls
.pending_num
)) {
1086 if (++thread_calls
.thread_num
> thread_calls
.thread_hiwat
)
1087 thread_calls
.thread_hiwat
= thread_calls
.thread_num
;
1089 simple_unlock(&thread_call_lock
);
1092 (void) kernel_thread_with_priority(
1093 kernel_task
, MAXPRI_KERNEL
- 1,
1094 _call_thread
, TRUE
, TRUE
);
1095 #if NO_CONTINUATIONS
1096 thread_block((void (*)(void)) 0);
1099 thread_block(_activate_thread_continue
);
1103 else if (thread_calls
.pending_num
> 0) {
1104 _call_thread_wake();
1106 simple_unlock(&thread_call_lock
);
1109 #if NO_CONTINUATIONS
1110 thread_block((void (*)(void)) 0);
1113 thread_block(_activate_thread_continue
);
1118 assert_wait(&activate_thread_awake
, THREAD_INTERRUPTIBLE
);
1119 activate_thread_awake
= FALSE
;
1121 simple_unlock(&thread_call_lock
);
1124 #if NO_CONTINUATIONS
1125 thread_block((void (*)(void)) 0);
1128 thread_block(_activate_thread_continue
);
1135 _activate_thread(void)
1137 thread_t self
= current_thread();
1139 self
->vm_privilege
= TRUE
;
1140 vm_page_free_reserve(2); /* XXX */
1141 stack_privilege(self
);
1143 _activate_thread_continue();
1149 _delayed_call_timer(
1150 timer_call_param_t p0
,
1151 timer_call_param_t p1
1156 boolean_t new_pending
= FALSE
;
1160 simple_lock(&thread_call_lock
);
1162 clock_get_uptime(×tamp
);
1164 call
= TC(queue_first(&delayed_call_queue
));
1166 while (!queue_end(&delayed_call_queue
, qe(call
))) {
1167 if (call
->deadline
<= timestamp
) {
1168 _delayed_call_dequeue(call
);
1170 _pending_call_enqueue(call
);
1176 call
= TC(queue_first(&delayed_call_queue
));
1179 if (!queue_end(&delayed_call_queue
, qe(call
)))
1180 _set_delayed_call_timer(call
);
1183 _call_thread_wake();
1185 simple_unlock(&thread_call_lock
);