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 #include <sys/kdebug.h>
49 #define internal_call_num 768
51 #define thread_call_thread_min 4
55 internal_call_storage
[internal_call_num
];
57 decl_simple_lock_data(static,thread_call_lock
)
61 thread_call_delaytimer
;
65 thread_call_xxx_queue
,
66 thread_call_pending_queue
, thread_call_delayed_queue
;
70 call_thread_waitqueue
;
74 activate_thread_awake
;
90 static __inline__ thread_call_t
91 _internal_call_allocate(void);
93 static __inline__
void
94 _internal_call_release(
98 static __inline__
void
99 _pending_call_enqueue(
102 _pending_call_dequeue(
105 _delayed_call_enqueue(
108 _delayed_call_dequeue(
112 static void __inline__
113 _set_delayed_call_timer(
118 _remove_from_pending_queue(
119 thread_call_func_t func
,
120 thread_call_param_t param0
,
123 _remove_from_delayed_queue(
124 thread_call_func_t func
,
125 thread_call_param_t param0
,
129 static __inline__
void
130 _call_thread_wake(void);
134 _activate_thread(void);
138 timer_call_param_t p0
,
139 timer_call_param_t p1
142 #define qe(x) ((queue_entry_t)(x))
143 #define TC(x) ((thread_call_t)(x))
146 * Routine: thread_call_initialize [public]
148 * Description: Initialize this module, called
149 * early during system initialization.
151 * Preconditions: None.
153 * Postconditions: None.
157 thread_call_initialize(void)
162 simple_lock_init(&thread_call_lock
, ETAP_MISC_TIMER
);
165 simple_lock(&thread_call_lock
);
167 queue_init(&thread_call_pending_queue
);
168 queue_init(&thread_call_delayed_queue
);
170 queue_init(&thread_call_xxx_queue
);
172 call
= internal_call_storage
;
173 call
< &internal_call_storage
[internal_call_num
];
176 enqueue_tail(&thread_call_xxx_queue
, qe(call
));
179 timer_call_setup(&thread_call_delaytimer
, _delayed_call_timer
, NULL
);
181 wait_queue_init(&call_thread_waitqueue
, SYNC_POLICY_FIFO
);
182 thread_call_vars
.thread_lowat
= thread_call_thread_min
;
184 activate_thread_awake
= TRUE
;
186 simple_unlock(&thread_call_lock
);
189 kernel_thread_with_priority(_activate_thread
, MAXPRI_KERNEL
- 2);
195 thread_call_func_t func
,
196 thread_call_param_t param0
199 call_entry_setup(call
, func
, param0
);
203 * Routine: _internal_call_allocate [private, inline]
205 * Purpose: Allocate an internal callout entry.
207 * Preconditions: thread_call_lock held.
209 * Postconditions: None.
212 static __inline__ thread_call_t
213 _internal_call_allocate(void)
217 if (queue_empty(&thread_call_xxx_queue
))
218 panic("_internal_call_allocate");
220 call
= TC(dequeue_head(&thread_call_xxx_queue
));
226 * Routine: _internal_call_release [private, inline]
228 * Purpose: Release an internal callout entry which
229 * is no longer pending (or delayed).
231 * Preconditions: thread_call_lock held.
233 * Postconditions: None.
238 _internal_call_release(
242 if ( call
>= internal_call_storage
&&
243 call
< &internal_call_storage
[internal_call_num
] )
244 enqueue_head(&thread_call_xxx_queue
, qe(call
));
248 * Routine: _pending_call_enqueue [private, inline]
250 * Purpose: Place an entry at the end of the
251 * pending queue, to be executed soon.
253 * Preconditions: thread_call_lock held.
255 * Postconditions: None.
260 _pending_call_enqueue(
264 enqueue_tail(&thread_call_pending_queue
, qe(call
));
265 if (++thread_call_vars
.pending_num
> thread_call_vars
.pending_hiwat
)
266 thread_call_vars
.pending_hiwat
= thread_call_vars
.pending_num
;
268 call
->state
= PENDING
;
272 * Routine: _pending_call_dequeue [private, inline]
274 * Purpose: Remove an entry from the pending queue,
275 * effectively unscheduling it.
277 * Preconditions: thread_call_lock held.
279 * Postconditions: None.
284 _pending_call_dequeue(
288 (void)remque(qe(call
));
289 thread_call_vars
.pending_num
--;
295 * Routine: _delayed_call_enqueue [private, inline]
297 * Purpose: Place an entry on the delayed queue,
298 * after existing entries with an earlier
299 * (or identical) deadline.
301 * Preconditions: thread_call_lock held.
303 * Postconditions: None.
308 _delayed_call_enqueue(
312 thread_call_t current
;
314 current
= TC(queue_first(&thread_call_delayed_queue
));
317 if ( queue_end(&thread_call_delayed_queue
, qe(current
)) ||
318 call
->deadline
< current
->deadline
) {
319 current
= TC(queue_prev(qe(current
)));
323 current
= TC(queue_next(qe(current
)));
326 insque(qe(call
), qe(current
));
327 if (++thread_call_vars
.delayed_num
> thread_call_vars
.delayed_hiwat
)
328 thread_call_vars
.delayed_hiwat
= thread_call_vars
.delayed_num
;
330 call
->state
= DELAYED
;
334 * Routine: _delayed_call_dequeue [private, inline]
336 * Purpose: Remove an entry from the delayed queue,
337 * effectively unscheduling it.
339 * Preconditions: thread_call_lock held.
341 * Postconditions: None.
346 _delayed_call_dequeue(
350 (void)remque(qe(call
));
351 thread_call_vars
.delayed_num
--;
357 * Routine: _set_delayed_call_timer [private]
359 * Purpose: Reset the timer so that it
360 * next expires when the entry is due.
362 * Preconditions: thread_call_lock held.
364 * Postconditions: None.
367 static __inline__
void
368 _set_delayed_call_timer(
372 timer_call_enter(&thread_call_delaytimer
, call
->deadline
);
376 * Routine: _remove_from_pending_queue [private]
378 * Purpose: Remove the first (or all) matching
379 * entries from the pending queue,
380 * effectively unscheduling them.
381 * Returns whether any matching entries
384 * Preconditions: thread_call_lock held.
386 * Postconditions: None.
391 _remove_from_pending_queue(
392 thread_call_func_t func
,
393 thread_call_param_t param0
,
397 boolean_t call_removed
= FALSE
;
400 call
= TC(queue_first(&thread_call_pending_queue
));
402 while (!queue_end(&thread_call_pending_queue
, qe(call
))) {
403 if ( call
->func
== func
&&
404 call
->param0
== param0
) {
405 thread_call_t next
= TC(queue_next(qe(call
)));
407 _pending_call_dequeue(call
);
409 _internal_call_release(call
);
418 call
= TC(queue_next(qe(call
)));
421 return (call_removed
);
425 * Routine: _remove_from_delayed_queue [private]
427 * Purpose: Remove the first (or all) matching
428 * entries from the delayed queue,
429 * effectively unscheduling them.
430 * Returns whether any matching entries
433 * Preconditions: thread_call_lock held.
435 * Postconditions: None.
440 _remove_from_delayed_queue(
441 thread_call_func_t func
,
442 thread_call_param_t param0
,
446 boolean_t call_removed
= FALSE
;
449 call
= TC(queue_first(&thread_call_delayed_queue
));
451 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
452 if ( call
->func
== func
&&
453 call
->param0
== param0
) {
454 thread_call_t next
= TC(queue_next(qe(call
)));
456 _delayed_call_dequeue(call
);
458 _internal_call_release(call
);
467 call
= TC(queue_next(qe(call
)));
470 return (call_removed
);
474 * Routine: thread_call_func [public]
476 * Purpose: Schedule a function callout.
477 * Guarantees { function, argument }
478 * uniqueness if unique_call is TRUE.
480 * Preconditions: Callable from an interrupt context
483 * Postconditions: None.
488 thread_call_func_t func
,
489 thread_call_param_t param
,
490 boolean_t unique_call
497 simple_lock(&thread_call_lock
);
499 call
= TC(queue_first(&thread_call_pending_queue
));
501 while (unique_call
&& !queue_end(&thread_call_pending_queue
, qe(call
))) {
502 if ( call
->func
== func
&&
503 call
->param0
== param
) {
507 call
= TC(queue_next(qe(call
)));
510 if (!unique_call
|| queue_end(&thread_call_pending_queue
, qe(call
))) {
511 call
= _internal_call_allocate();
513 call
->param0
= param
;
516 _pending_call_enqueue(call
);
518 if (thread_call_vars
.active_num
<= 0)
522 simple_unlock(&thread_call_lock
);
527 * Routine: thread_call_func_delayed [public]
529 * Purpose: Schedule a function callout to
530 * occur at the stated time.
532 * Preconditions: Callable from an interrupt context
535 * Postconditions: None.
539 thread_call_func_delayed(
540 thread_call_func_t func
,
541 thread_call_param_t param
,
549 simple_lock(&thread_call_lock
);
551 call
= _internal_call_allocate();
553 call
->param0
= param
;
555 call
->deadline
= deadline
;
557 _delayed_call_enqueue(call
);
559 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
560 _set_delayed_call_timer(call
);
562 simple_unlock(&thread_call_lock
);
567 * Routine: thread_call_func_cancel [public]
569 * Purpose: Unschedule a function callout.
570 * Removes one (or all)
571 * { function, argument }
572 * instance(s) from either (or both)
573 * the pending and the delayed queue,
574 * in that order. Returns a boolean
575 * indicating whether any calls were
578 * Preconditions: Callable from an interrupt context
581 * Postconditions: None.
585 thread_call_func_cancel(
586 thread_call_func_t func
,
587 thread_call_param_t param
,
595 simple_lock(&thread_call_lock
);
598 result
= _remove_from_pending_queue(func
, param
, cancel_all
) |
599 _remove_from_delayed_queue(func
, param
, cancel_all
);
601 result
= _remove_from_pending_queue(func
, param
, cancel_all
) ||
602 _remove_from_delayed_queue(func
, param
, cancel_all
);
604 simple_unlock(&thread_call_lock
);
611 * Routine: thread_call_allocate [public]
613 * Purpose: Allocate an external callout
616 * Preconditions: None.
618 * Postconditions: None.
622 thread_call_allocate(
623 thread_call_func_t func
,
624 thread_call_param_t param0
627 thread_call_t call
= (void *)kalloc(sizeof (thread_call_data_t
));
630 call
->param0
= param0
;
637 * Routine: thread_call_free [public]
639 * Purpose: Free an external callout
642 * Preconditions: None.
644 * Postconditions: None.
655 simple_lock(&thread_call_lock
);
657 if (call
->state
!= IDLE
) {
658 simple_unlock(&thread_call_lock
);
664 simple_unlock(&thread_call_lock
);
667 kfree((vm_offset_t
)call
, sizeof (thread_call_data_t
));
673 * Routine: thread_call_enter [public]
675 * Purpose: Schedule an external callout
676 * entry to occur "soon". Returns a
677 * boolean indicating whether the call
678 * had been already scheduled.
680 * Preconditions: Callable from an interrupt context
683 * Postconditions: None.
691 boolean_t result
= TRUE
;
695 simple_lock(&thread_call_lock
);
697 if (call
->state
!= PENDING
) {
698 if (call
->state
== DELAYED
)
699 _delayed_call_dequeue(call
);
700 else if (call
->state
== IDLE
)
703 _pending_call_enqueue(call
);
705 if (thread_call_vars
.active_num
<= 0)
711 simple_unlock(&thread_call_lock
);
720 thread_call_param_t param1
723 boolean_t result
= TRUE
;
727 simple_lock(&thread_call_lock
);
729 if (call
->state
!= PENDING
) {
730 if (call
->state
== DELAYED
)
731 _delayed_call_dequeue(call
);
732 else if (call
->state
== IDLE
)
735 _pending_call_enqueue(call
);
737 if (thread_call_vars
.active_num
<= 0)
741 call
->param1
= param1
;
743 simple_unlock(&thread_call_lock
);
750 * Routine: thread_call_enter_delayed [public]
752 * Purpose: Schedule an external callout
753 * entry to occur at the stated time.
754 * Returns a boolean indicating whether
755 * the call had been already scheduled.
757 * Preconditions: Callable from an interrupt context
760 * Postconditions: None.
764 thread_call_enter_delayed(
769 boolean_t result
= TRUE
;
773 simple_lock(&thread_call_lock
);
775 if (call
->state
== PENDING
)
776 _pending_call_dequeue(call
);
777 else if (call
->state
== DELAYED
)
778 _delayed_call_dequeue(call
);
779 else if (call
->state
== IDLE
)
783 call
->deadline
= deadline
;
785 _delayed_call_enqueue(call
);
787 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
788 _set_delayed_call_timer(call
);
790 simple_unlock(&thread_call_lock
);
797 thread_call_enter1_delayed(
799 thread_call_param_t param1
,
803 boolean_t result
= TRUE
;
807 simple_lock(&thread_call_lock
);
809 if (call
->state
== PENDING
)
810 _pending_call_dequeue(call
);
811 else if (call
->state
== DELAYED
)
812 _delayed_call_dequeue(call
);
813 else if (call
->state
== IDLE
)
816 call
->param1
= param1
;
817 call
->deadline
= deadline
;
819 _delayed_call_enqueue(call
);
821 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
822 _set_delayed_call_timer(call
);
824 simple_unlock(&thread_call_lock
);
831 * Routine: thread_call_cancel [public]
833 * Purpose: Unschedule a callout entry.
834 * Returns a boolean indicating
835 * whether the call had actually
838 * Preconditions: Callable from an interrupt context
841 * Postconditions: None.
849 boolean_t result
= TRUE
;
853 simple_lock(&thread_call_lock
);
855 if (call
->state
== PENDING
)
856 _pending_call_dequeue(call
);
857 else if (call
->state
== DELAYED
)
858 _delayed_call_dequeue(call
);
862 simple_unlock(&thread_call_lock
);
869 * Routine: thread_call_is_delayed [public]
871 * Purpose: Returns a boolean indicating
872 * whether a call is currently scheduled
873 * to occur at a later time. Optionally
874 * returns the expiration time.
876 * Preconditions: Callable from an interrupt context
879 * Postconditions: None.
883 thread_call_is_delayed(
887 boolean_t result
= FALSE
;
891 simple_lock(&thread_call_lock
);
893 if (call
->state
== DELAYED
) {
894 if (deadline
!= NULL
)
895 *deadline
= call
->deadline
;
899 simple_unlock(&thread_call_lock
);
906 * Routine: _call_thread_wake [private, inline]
908 * Purpose: Wake a callout thread to service
909 * pending callout entries. May wake
910 * the activate thread in order to
911 * create additional callout threads.
913 * Preconditions: thread_call_lock held.
915 * Postconditions: None.
920 _call_thread_wake(void)
922 if (wait_queue_wakeup_one(
923 &call_thread_waitqueue
, &call_thread_waitqueue
,
924 THREAD_AWAKENED
) == KERN_SUCCESS
) {
925 thread_call_vars
.idle_thread_num
--;
927 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
928 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
931 if (!activate_thread_awake
) {
932 thread_wakeup_one(&activate_thread_awake
);
933 activate_thread_awake
= TRUE
;
938 * Routine: call_thread_block [private]
940 * Purpose: Hook via thread dispatch on
941 * the occasion of a callout blocking.
943 * Preconditions: splsched.
945 * Postconditions: None.
949 call_thread_block(void)
951 simple_lock(&thread_call_lock
);
953 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
954 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
956 if ( thread_call_vars
.active_num
<= 0 &&
957 thread_call_vars
.pending_num
> 0 )
960 simple_unlock(&thread_call_lock
);
964 * Routine: call_thread_unblock [private]
966 * Purpose: Hook via thread wakeup on
967 * the occasion of a callout unblocking.
969 * Preconditions: splsched.
971 * Postconditions: None.
975 call_thread_unblock(void)
977 simple_lock(&thread_call_lock
);
979 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
980 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
982 simple_unlock(&thread_call_lock
);
986 * Routine: _call_thread [private]
988 * Purpose: Executed by a callout thread.
990 * Preconditions: None.
992 * Postconditions: None.
997 _call_thread_continue(void)
999 thread_t self
= current_thread();
1002 simple_lock(&thread_call_lock
);
1004 self
->active_callout
= TRUE
;
1006 while (thread_call_vars
.pending_num
> 0) {
1008 thread_call_func_t func
;
1009 thread_call_param_t param0
, param1
;
1011 call
= TC(dequeue_head(&thread_call_pending_queue
));
1012 thread_call_vars
.pending_num
--;
1015 param0
= call
->param0
;
1016 param1
= call
->param1
;
1020 _internal_call_release(call
);
1022 simple_unlock(&thread_call_lock
);
1025 KERNEL_DEBUG_CONSTANT(
1026 MACHDBG_CODE(DBG_MACH_SCHED
,MACH_CALLOUT
) | DBG_FUNC_NONE
,
1027 (int)func
, (int)param0
, (int)param1
, 0, 0);
1029 (*func
)(param0
, param1
);
1031 (void)thread_funnel_set(self
->funnel_lock
, FALSE
);
1034 simple_lock(&thread_call_lock
);
1037 self
->active_callout
= FALSE
;
1039 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
1040 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
1042 if (thread_call_vars
.idle_thread_num
< thread_call_vars
.thread_lowat
) {
1043 thread_call_vars
.idle_thread_num
++;
1045 wait_queue_assert_wait(
1046 &call_thread_waitqueue
, &call_thread_waitqueue
,
1047 THREAD_INTERRUPTIBLE
);
1049 simple_unlock(&thread_call_lock
);
1052 thread_block(_call_thread_continue
);
1056 thread_call_vars
.thread_num
--;
1058 simple_unlock(&thread_call_lock
);
1061 (void) thread_terminate(self
->top_act
);
1069 _call_thread_continue();
1074 * Routine: _activate_thread [private]
1076 * Purpose: Executed by the activate thread.
1078 * Preconditions: None.
1080 * Postconditions: Never terminates.
1085 _activate_thread_continue(void)
1088 simple_lock(&thread_call_lock
);
1090 while ( thread_call_vars
.active_num
<= 0 &&
1091 thread_call_vars
.pending_num
> 0 ) {
1093 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
1094 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
1096 if (++thread_call_vars
.thread_num
> thread_call_vars
.thread_hiwat
)
1097 thread_call_vars
.thread_hiwat
= thread_call_vars
.thread_num
;
1099 simple_unlock(&thread_call_lock
);
1102 kernel_thread_with_priority(_call_thread
, MAXPRI_KERNEL
- 1);
1105 simple_lock(&thread_call_lock
);
1108 assert_wait(&activate_thread_awake
, THREAD_INTERRUPTIBLE
);
1109 activate_thread_awake
= FALSE
;
1111 simple_unlock(&thread_call_lock
);
1114 thread_block(_activate_thread_continue
);
1120 _activate_thread(void)
1122 thread_t self
= current_thread();
1124 self
->vm_privilege
= TRUE
;
1125 vm_page_free_reserve(2); /* XXX */
1127 _activate_thread_continue();
1133 _delayed_call_timer(
1134 timer_call_param_t p0
,
1135 timer_call_param_t p1
1140 boolean_t new_pending
= FALSE
;
1144 simple_lock(&thread_call_lock
);
1146 clock_get_uptime(×tamp
);
1148 call
= TC(queue_first(&thread_call_delayed_queue
));
1150 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
1151 if (call
->deadline
<= timestamp
) {
1152 _delayed_call_dequeue(call
);
1154 _pending_call_enqueue(call
);
1160 call
= TC(queue_first(&thread_call_delayed_queue
));
1163 if (!queue_end(&thread_call_delayed_queue
, qe(call
)))
1164 _set_delayed_call_timer(call
);
1166 if (new_pending
&& thread_call_vars
.active_num
<= 0)
1167 _call_thread_wake();
1169 simple_unlock(&thread_call_lock
);