2 * Copyright (c) 1993-1995, 1999-2005 Apple Computer, Inc.
5 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. The rights granted to you under the License
11 * may not be used to create, or enable the creation or redistribution of,
12 * unlawful or unlicensed copies of an Apple operating system, or to
13 * circumvent, violate, or enable the circumvention or violation of, any
14 * terms of an Apple operating system software license agreement.
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this file.
19 * The Original Code and all software distributed under the License are
20 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
24 * Please see the License for the specific language governing rights and
25 * limitations under the License.
27 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <mach/mach_types.h>
31 #include <mach/thread_act.h>
33 #include <kern/kern_types.h>
34 #include <kern/kalloc.h>
35 #include <kern/sched_prim.h>
36 #include <kern/clock.h>
37 #include <kern/task.h>
38 #include <kern/thread.h>
39 #include <kern/wait_queue.h>
41 #include <vm/vm_pageout.h>
43 #include <kern/thread_call.h>
44 #include <kern/call_entry.h>
46 #include <kern/timer_call.h>
48 #include <sys/kdebug.h>
50 #define internal_call_num 768
52 #define thread_call_thread_min 4
56 internal_call_storage
[internal_call_num
];
58 decl_simple_lock_data(static,thread_call_lock
)
62 thread_call_delaytimer
;
66 thread_call_xxx_queue
,
67 thread_call_pending_queue
, thread_call_delayed_queue
;
71 call_thread_waitqueue
;
75 activate_thread_awake
;
91 static __inline__ thread_call_t
92 _internal_call_allocate(void);
94 static __inline__
void
95 _internal_call_release(
99 static __inline__
void
100 _pending_call_enqueue(
103 _pending_call_dequeue(
106 _delayed_call_enqueue(
109 _delayed_call_dequeue(
113 static __inline__
void
114 _set_delayed_call_timer(
119 _remove_from_pending_queue(
120 thread_call_func_t func
,
121 thread_call_param_t param0
,
124 _remove_from_delayed_queue(
125 thread_call_func_t func
,
126 thread_call_param_t param0
,
130 static __inline__
void
131 _call_thread_wake(void);
135 _activate_thread(void);
139 timer_call_param_t p0
,
140 timer_call_param_t p1
143 #define qe(x) ((queue_entry_t)(x))
144 #define TC(x) ((thread_call_t)(x))
147 * Routine: thread_call_initialize [public]
149 * Description: Initialize this module, called
150 * early during system initialization.
152 * Preconditions: None.
154 * Postconditions: None.
158 thread_call_initialize(void)
160 kern_return_t result
;
165 simple_lock_init(&thread_call_lock
, 0);
168 simple_lock(&thread_call_lock
);
170 queue_init(&thread_call_pending_queue
);
171 queue_init(&thread_call_delayed_queue
);
173 queue_init(&thread_call_xxx_queue
);
175 call
= internal_call_storage
;
176 call
< &internal_call_storage
[internal_call_num
];
179 enqueue_tail(&thread_call_xxx_queue
, qe(call
));
182 timer_call_setup(&thread_call_delaytimer
, _delayed_call_timer
, NULL
);
184 wait_queue_init(&call_thread_waitqueue
, SYNC_POLICY_FIFO
);
185 thread_call_vars
.thread_lowat
= thread_call_thread_min
;
187 activate_thread_awake
= TRUE
;
189 simple_unlock(&thread_call_lock
);
192 result
= kernel_thread_start_priority((thread_continue_t
)_activate_thread
, NULL
, MAXPRI_KERNEL
- 2, &thread
);
193 if (result
!= KERN_SUCCESS
)
194 panic("thread_call_initialize");
196 thread_deallocate(thread
);
202 thread_call_func_t func
,
203 thread_call_param_t param0
206 call_entry_setup(call
, func
, param0
);
210 * Routine: _internal_call_allocate [private, inline]
212 * Purpose: Allocate an internal callout entry.
214 * Preconditions: thread_call_lock held.
216 * Postconditions: None.
219 static __inline__ thread_call_t
220 _internal_call_allocate(void)
224 if (queue_empty(&thread_call_xxx_queue
))
225 panic("_internal_call_allocate");
227 call
= TC(dequeue_head(&thread_call_xxx_queue
));
233 * Routine: _internal_call_release [private, inline]
235 * Purpose: Release an internal callout entry which
236 * is no longer pending (or delayed).
238 * Preconditions: thread_call_lock held.
240 * Postconditions: None.
245 _internal_call_release(
249 if ( call
>= internal_call_storage
&&
250 call
< &internal_call_storage
[internal_call_num
] )
251 enqueue_head(&thread_call_xxx_queue
, qe(call
));
255 * Routine: _pending_call_enqueue [private, inline]
257 * Purpose: Place an entry at the end of the
258 * pending queue, to be executed soon.
260 * Preconditions: thread_call_lock held.
262 * Postconditions: None.
267 _pending_call_enqueue(
271 enqueue_tail(&thread_call_pending_queue
, qe(call
));
272 if (++thread_call_vars
.pending_num
> thread_call_vars
.pending_hiwat
)
273 thread_call_vars
.pending_hiwat
= thread_call_vars
.pending_num
;
275 call
->state
= PENDING
;
279 * Routine: _pending_call_dequeue [private, inline]
281 * Purpose: Remove an entry from the pending queue,
282 * effectively unscheduling it.
284 * Preconditions: thread_call_lock held.
286 * Postconditions: None.
291 _pending_call_dequeue(
295 (void)remque(qe(call
));
296 thread_call_vars
.pending_num
--;
302 * Routine: _delayed_call_enqueue [private, inline]
304 * Purpose: Place an entry on the delayed queue,
305 * after existing entries with an earlier
306 * (or identical) deadline.
308 * Preconditions: thread_call_lock held.
310 * Postconditions: None.
315 _delayed_call_enqueue(
319 thread_call_t current
;
321 current
= TC(queue_first(&thread_call_delayed_queue
));
324 if ( queue_end(&thread_call_delayed_queue
, qe(current
)) ||
325 call
->deadline
< current
->deadline
) {
326 current
= TC(queue_prev(qe(current
)));
330 current
= TC(queue_next(qe(current
)));
333 insque(qe(call
), qe(current
));
334 if (++thread_call_vars
.delayed_num
> thread_call_vars
.delayed_hiwat
)
335 thread_call_vars
.delayed_hiwat
= thread_call_vars
.delayed_num
;
337 call
->state
= DELAYED
;
341 * Routine: _delayed_call_dequeue [private, inline]
343 * Purpose: Remove an entry from the delayed queue,
344 * effectively unscheduling it.
346 * Preconditions: thread_call_lock held.
348 * Postconditions: None.
353 _delayed_call_dequeue(
357 (void)remque(qe(call
));
358 thread_call_vars
.delayed_num
--;
364 * Routine: _set_delayed_call_timer [private]
366 * Purpose: Reset the timer so that it
367 * next expires when the entry is due.
369 * Preconditions: thread_call_lock held.
371 * Postconditions: None.
374 static __inline__
void
375 _set_delayed_call_timer(
379 timer_call_enter(&thread_call_delaytimer
, call
->deadline
);
383 * Routine: _remove_from_pending_queue [private]
385 * Purpose: Remove the first (or all) matching
386 * entries from the pending queue,
387 * effectively unscheduling them.
388 * Returns whether any matching entries
391 * Preconditions: thread_call_lock held.
393 * Postconditions: None.
398 _remove_from_pending_queue(
399 thread_call_func_t func
,
400 thread_call_param_t param0
,
404 boolean_t call_removed
= FALSE
;
407 call
= TC(queue_first(&thread_call_pending_queue
));
409 while (!queue_end(&thread_call_pending_queue
, qe(call
))) {
410 if ( call
->func
== func
&&
411 call
->param0
== param0
) {
412 thread_call_t next
= TC(queue_next(qe(call
)));
414 _pending_call_dequeue(call
);
416 _internal_call_release(call
);
425 call
= TC(queue_next(qe(call
)));
428 return (call_removed
);
432 * Routine: _remove_from_delayed_queue [private]
434 * Purpose: Remove the first (or all) matching
435 * entries from the delayed queue,
436 * effectively unscheduling them.
437 * Returns whether any matching entries
440 * Preconditions: thread_call_lock held.
442 * Postconditions: None.
447 _remove_from_delayed_queue(
448 thread_call_func_t func
,
449 thread_call_param_t param0
,
453 boolean_t call_removed
= FALSE
;
456 call
= TC(queue_first(&thread_call_delayed_queue
));
458 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
459 if ( call
->func
== func
&&
460 call
->param0
== param0
) {
461 thread_call_t next
= TC(queue_next(qe(call
)));
463 _delayed_call_dequeue(call
);
465 _internal_call_release(call
);
474 call
= TC(queue_next(qe(call
)));
477 return (call_removed
);
481 * Routine: thread_call_func [public]
483 * Purpose: Schedule a function callout.
484 * Guarantees { function, argument }
485 * uniqueness if unique_call is TRUE.
487 * Preconditions: Callable from an interrupt context
490 * Postconditions: None.
495 thread_call_func_t func
,
496 thread_call_param_t param
,
497 boolean_t unique_call
504 simple_lock(&thread_call_lock
);
506 call
= TC(queue_first(&thread_call_pending_queue
));
508 while (unique_call
&& !queue_end(&thread_call_pending_queue
, qe(call
))) {
509 if ( call
->func
== func
&&
510 call
->param0
== param
) {
514 call
= TC(queue_next(qe(call
)));
517 if (!unique_call
|| queue_end(&thread_call_pending_queue
, qe(call
))) {
518 call
= _internal_call_allocate();
520 call
->param0
= param
;
523 _pending_call_enqueue(call
);
525 if (thread_call_vars
.active_num
<= 0)
529 simple_unlock(&thread_call_lock
);
534 * Routine: thread_call_func_delayed [public]
536 * Purpose: Schedule a function callout to
537 * occur at the stated time.
539 * Preconditions: Callable from an interrupt context
542 * Postconditions: None.
546 thread_call_func_delayed(
547 thread_call_func_t func
,
548 thread_call_param_t param
,
556 simple_lock(&thread_call_lock
);
558 call
= _internal_call_allocate();
560 call
->param0
= param
;
562 call
->deadline
= deadline
;
564 _delayed_call_enqueue(call
);
566 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
567 _set_delayed_call_timer(call
);
569 simple_unlock(&thread_call_lock
);
574 * Routine: thread_call_func_cancel [public]
576 * Purpose: Unschedule a function callout.
577 * Removes one (or all)
578 * { function, argument }
579 * instance(s) from either (or both)
580 * the pending and the delayed queue,
581 * in that order. Returns a boolean
582 * indicating whether any calls were
585 * Preconditions: Callable from an interrupt context
588 * Postconditions: None.
592 thread_call_func_cancel(
593 thread_call_func_t func
,
594 thread_call_param_t param
,
602 simple_lock(&thread_call_lock
);
605 result
= _remove_from_pending_queue(func
, param
, cancel_all
) |
606 _remove_from_delayed_queue(func
, param
, cancel_all
);
608 result
= _remove_from_pending_queue(func
, param
, cancel_all
) ||
609 _remove_from_delayed_queue(func
, param
, cancel_all
);
611 simple_unlock(&thread_call_lock
);
618 * Routine: thread_call_allocate [public]
620 * Purpose: Allocate an external callout
623 * Preconditions: None.
625 * Postconditions: None.
629 thread_call_allocate(
630 thread_call_func_t func
,
631 thread_call_param_t param0
634 thread_call_t call
= (void *)kalloc(sizeof (thread_call_data_t
));
637 call
->param0
= param0
;
644 * Routine: thread_call_free [public]
646 * Purpose: Free an external callout
649 * Preconditions: None.
651 * Postconditions: None.
662 simple_lock(&thread_call_lock
);
664 if (call
->state
!= IDLE
) {
665 simple_unlock(&thread_call_lock
);
671 simple_unlock(&thread_call_lock
);
674 kfree(call
, sizeof (thread_call_data_t
));
680 * Routine: thread_call_enter [public]
682 * Purpose: Schedule an external callout
683 * entry to occur "soon". Returns a
684 * boolean indicating whether the call
685 * had been already scheduled.
687 * Preconditions: Callable from an interrupt context
690 * Postconditions: None.
698 boolean_t result
= TRUE
;
702 simple_lock(&thread_call_lock
);
704 if (call
->state
!= PENDING
) {
705 if (call
->state
== DELAYED
)
706 _delayed_call_dequeue(call
);
707 else if (call
->state
== IDLE
)
710 _pending_call_enqueue(call
);
712 if (thread_call_vars
.active_num
<= 0)
718 simple_unlock(&thread_call_lock
);
727 thread_call_param_t param1
730 boolean_t result
= TRUE
;
734 simple_lock(&thread_call_lock
);
736 if (call
->state
!= PENDING
) {
737 if (call
->state
== DELAYED
)
738 _delayed_call_dequeue(call
);
739 else if (call
->state
== IDLE
)
742 _pending_call_enqueue(call
);
744 if (thread_call_vars
.active_num
<= 0)
748 call
->param1
= param1
;
750 simple_unlock(&thread_call_lock
);
757 * Routine: thread_call_enter_delayed [public]
759 * Purpose: Schedule an external callout
760 * entry to occur at the stated time.
761 * Returns a boolean indicating whether
762 * the call had been already scheduled.
764 * Preconditions: Callable from an interrupt context
767 * Postconditions: None.
771 thread_call_enter_delayed(
776 boolean_t result
= TRUE
;
780 simple_lock(&thread_call_lock
);
782 if (call
->state
== PENDING
)
783 _pending_call_dequeue(call
);
784 else if (call
->state
== DELAYED
)
785 _delayed_call_dequeue(call
);
786 else if (call
->state
== IDLE
)
790 call
->deadline
= deadline
;
792 _delayed_call_enqueue(call
);
794 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
795 _set_delayed_call_timer(call
);
797 simple_unlock(&thread_call_lock
);
804 thread_call_enter1_delayed(
806 thread_call_param_t param1
,
810 boolean_t result
= TRUE
;
814 simple_lock(&thread_call_lock
);
816 if (call
->state
== PENDING
)
817 _pending_call_dequeue(call
);
818 else if (call
->state
== DELAYED
)
819 _delayed_call_dequeue(call
);
820 else if (call
->state
== IDLE
)
823 call
->param1
= param1
;
824 call
->deadline
= deadline
;
826 _delayed_call_enqueue(call
);
828 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
829 _set_delayed_call_timer(call
);
831 simple_unlock(&thread_call_lock
);
838 * Routine: thread_call_cancel [public]
840 * Purpose: Unschedule a callout entry.
841 * Returns a boolean indicating
842 * whether the call had actually
845 * Preconditions: Callable from an interrupt context
848 * Postconditions: None.
856 boolean_t result
= TRUE
;
860 simple_lock(&thread_call_lock
);
862 if (call
->state
== PENDING
)
863 _pending_call_dequeue(call
);
864 else if (call
->state
== DELAYED
)
865 _delayed_call_dequeue(call
);
869 simple_unlock(&thread_call_lock
);
876 * Routine: thread_call_is_delayed [public]
878 * Purpose: Returns a boolean indicating
879 * whether a call is currently scheduled
880 * to occur at a later time. Optionally
881 * returns the expiration time.
883 * Preconditions: Callable from an interrupt context
886 * Postconditions: None.
890 thread_call_is_delayed(
894 boolean_t result
= FALSE
;
898 simple_lock(&thread_call_lock
);
900 if (call
->state
== DELAYED
) {
901 if (deadline
!= NULL
)
902 *deadline
= call
->deadline
;
906 simple_unlock(&thread_call_lock
);
913 * Routine: _call_thread_wake [private, inline]
915 * Purpose: Wake a callout thread to service
916 * pending callout entries. May wake
917 * the activate thread in order to
918 * create additional callout threads.
920 * Preconditions: thread_call_lock held.
922 * Postconditions: None.
927 _call_thread_wake(void)
929 if (wait_queue_wakeup_one(&call_thread_waitqueue
, NULL
, THREAD_AWAKENED
) == KERN_SUCCESS
) {
930 thread_call_vars
.idle_thread_num
--;
932 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
933 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
936 if (!activate_thread_awake
) {
937 thread_wakeup_one(&activate_thread_awake
);
938 activate_thread_awake
= TRUE
;
943 * Routine: call_thread_block [private]
945 * Purpose: Hook via thread dispatch on
946 * the occasion of a callout blocking.
948 * Preconditions: splsched.
950 * Postconditions: None.
954 call_thread_block(void)
956 simple_lock(&thread_call_lock
);
958 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
959 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
961 if ( thread_call_vars
.active_num
<= 0 &&
962 thread_call_vars
.pending_num
> 0 )
965 simple_unlock(&thread_call_lock
);
969 * Routine: call_thread_unblock [private]
971 * Purpose: Hook via thread wakeup on
972 * the occasion of a callout unblocking.
974 * Preconditions: splsched.
976 * Postconditions: None.
980 call_thread_unblock(void)
982 simple_lock(&thread_call_lock
);
984 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
985 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
987 simple_unlock(&thread_call_lock
);
991 * Routine: _call_thread [private]
993 * Purpose: Executed by a callout thread.
995 * Preconditions: None.
997 * Postconditions: None.
1002 _call_thread_continue(void)
1004 thread_t self
= current_thread();
1007 simple_lock(&thread_call_lock
);
1009 self
->options
|= TH_OPT_CALLOUT
;
1011 while (thread_call_vars
.pending_num
> 0) {
1013 thread_call_func_t func
;
1014 thread_call_param_t param0
, param1
;
1016 call
= TC(dequeue_head(&thread_call_pending_queue
));
1017 thread_call_vars
.pending_num
--;
1020 param0
= call
->param0
;
1021 param1
= call
->param1
;
1025 _internal_call_release(call
);
1027 simple_unlock(&thread_call_lock
);
1030 KERNEL_DEBUG_CONSTANT(
1031 MACHDBG_CODE(DBG_MACH_SCHED
,MACH_CALLOUT
) | DBG_FUNC_NONE
,
1032 (int)func
, (int)param0
, (int)param1
, 0, 0);
1034 (*func
)(param0
, param1
);
1036 (void)thread_funnel_set(self
->funnel_lock
, FALSE
);
1039 simple_lock(&thread_call_lock
);
1042 self
->options
&= ~TH_OPT_CALLOUT
;
1044 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
1045 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
1047 if (thread_call_vars
.idle_thread_num
< thread_call_vars
.thread_lowat
) {
1048 thread_call_vars
.idle_thread_num
++;
1050 wait_queue_assert_wait(&call_thread_waitqueue
, NULL
, THREAD_UNINT
, 0);
1052 simple_unlock(&thread_call_lock
);
1055 thread_block((thread_continue_t
)_call_thread_continue
);
1059 thread_call_vars
.thread_num
--;
1061 simple_unlock(&thread_call_lock
);
1064 thread_terminate(self
);
1072 _call_thread_continue();
1077 * Routine: _activate_thread [private]
1079 * Purpose: Executed by the activate thread.
1081 * Preconditions: None.
1083 * Postconditions: Never terminates.
1088 _activate_thread_continue(void)
1090 kern_return_t result
;
1094 simple_lock(&thread_call_lock
);
1096 while ( thread_call_vars
.active_num
<= 0 &&
1097 thread_call_vars
.pending_num
> 0 ) {
1099 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
1100 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
1102 if (++thread_call_vars
.thread_num
> thread_call_vars
.thread_hiwat
)
1103 thread_call_vars
.thread_hiwat
= thread_call_vars
.thread_num
;
1105 simple_unlock(&thread_call_lock
);
1108 result
= kernel_thread_start_priority((thread_continue_t
)_call_thread
, NULL
, MAXPRI_KERNEL
- 1, &thread
);
1109 if (result
!= KERN_SUCCESS
)
1110 panic("activate_thread");
1112 thread_deallocate(thread
);
1115 simple_lock(&thread_call_lock
);
1118 assert_wait(&activate_thread_awake
, THREAD_INTERRUPTIBLE
);
1119 activate_thread_awake
= FALSE
;
1121 simple_unlock(&thread_call_lock
);
1124 thread_block((thread_continue_t
)_activate_thread_continue
);
1130 _activate_thread(void)
1132 thread_t self
= current_thread();
1134 self
->options
|= TH_OPT_VMPRIV
;
1135 vm_page_free_reserve(2); /* XXX */
1137 _activate_thread_continue();
1143 _delayed_call_timer(
1144 __unused timer_call_param_t p0
,
1145 __unused timer_call_param_t p1
1150 boolean_t new_pending
= FALSE
;
1154 simple_lock(&thread_call_lock
);
1156 clock_get_uptime(×tamp
);
1158 call
= TC(queue_first(&thread_call_delayed_queue
));
1160 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
1161 if (call
->deadline
<= timestamp
) {
1162 _delayed_call_dequeue(call
);
1164 _pending_call_enqueue(call
);
1170 call
= TC(queue_first(&thread_call_delayed_queue
));
1173 if (!queue_end(&thread_call_delayed_queue
, qe(call
)))
1174 _set_delayed_call_timer(call
);
1176 if (new_pending
&& thread_call_vars
.active_num
<= 0)
1177 _call_thread_wake();
1179 simple_unlock(&thread_call_lock
);